V 1.0 Programming III. Automatic notifications (…Changed, INofityPropertyChanged,...

24
V 1.0 Programming III. Automatic notifications (…Changed, INofityPropertyChanged, ObservableCollection<T>) Data formatters Data conversions Resources

Transcript of V 1.0 Programming III. Automatic notifications (…Changed, INofityPropertyChanged,...

V 1.0

Programming III.

Automatic notifications (…Changed, INofityPropertyChanged, ObservableCollection<T>)

Data formattersData conversions

Resources

V 1.0 ÓE-NIK, 2014

Automatic notifications

• For the bindings to work, the binding target must change its value if the source changes its value– If we use the ElementName binding as a source, then it is pre-

programmed into the source UI element…• E.g. Slider.Value was the source, Label.Content was the target

– … or we used the UI elements as pure input fields, so the source object (DataContext) was only changed by the UI element values, and not the other way around

• If we use our own classes as binding sources, then (by default) the changes in the properties will not be reflected in the target UI elements!

2

V 1.0 ÓE-NIK, 2014

Example

• The person’s name is in the label (via DataContext), the button changes the name

• XAML code:

3

public MainWindow(){ InitializeComponent(); currentPerson = new Person("Piros Péter"); this.DataContext = currentPerson;}

<Label Content="{Binding Name}"/>

V 1.0 ÓE-NIK, 2014

Example

• The Click event handler of the button:

• The console shows the new value…

• … but the label still shows the old name!4

private void Button_Click(object sender, RoutedEventArgs e){ currentPerson.Name = "Kék Péter"; Console.WriteLine(currentPerson.Name); }

V 1.0 ÓE-NIK, 2014

Automatic notifications

• Módszerek:1. Dependency properties are capable of sending automatic

notifications on their own. So it works if the source class’s data property is not a normal property, but a dependency property! (we are not discussing this)

2. Raise the …Changed event if the property changes3. Implement the INotifyPropertyChanged interface to

report property changes through an universal event

5

V 1.0 ÓE-NIK, 2014

Notification – …Changed event• If the property changes, we should fire a …Changed

event (…=the name of the property)• Subscription to the event is automatic if a binding is

defined onto that property

6

public event EventHandler NameChanged;public string Name{ get { return name; } set { name = value; EventHandler nameChanged = NameChanged; if (nameChanged != null) nameChanged(this, EventArgs.Empty); }}

V 1.0 ÓE-NIK, 2014

Notification – INotifyPropertyChanged• The class should implement the

INotifyPropertyChanged interface (System.ComponentModel)• This interface only contains one event:

– public event PropertyChangedEventHandler PropertyChanged;• This event should be fired in the setter, the property

name is transmitted in the EventArgs

7

public string Name{ get { return name; } set { name = value; PropertyChangedEventHandler handler = PropertyChanged; if (handler != null) handler(this, new PropertyChangedEventArgs("Name")); }}

V 1.0 ÓE-NIK, 2014

Notification – INotifyPropertyChanged• Advantage: fewer events• Usually we also implement a helper/caller method:

OnPropertyChanged()

8

private void OnPropertyChanged(string propertyName){ PropertyChangedEventHandler handler = PropertyChanged; if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));}

public string Name{ get { return name; } set { name = value; OnPropertyChanged(“Name"); }}

V 1.0 ÓE-NIK, 2014

Notifications – Problems with collections

• With collections, we usually do not have a public setter

• Even if we had, the setter’s OnPropertyChanged will only be called if the whole list is re-created

• This will not detect:– If we replace one of the sub-items– Add()/Remove()/Insert()…– If a property of one of the sub-items changes

9

public List<Subscriptions> NewsletterSubscriptions{ get { return newsletterSubscriptions; } set {

newsletterSubscriptions = value; OnPropertyChanged(“NewsletterSubscriptions"); //??? }}

V 1.0 ÓE-NIK, 2014

Notifications – Problems with collections

10

List<Subscription> uj = new List<Subscription>();uj.Add(new Subscription("gyereknevelés", true));currentPerson.NewsletterSubscriptions = uj; //setter called event called currentPerson.NewsletterSubscriptions[0] = new Subscription("gyereknevelés", true); //not calledcurrentPerson.NewsletterSubscriptions.Add( new Subscription("gyereknevelés", true)); //not called

currentPerson.NewsletterSubscriptions[0].Active = false; //not called

V 1.0 ÓE-NIK, 2014

Notifications – ObservableCollection<T>• Exactly the same methods as List<T>

– System.Collections.ObjectModel

• Will send a notification:– If one of the sub-items is changed– Add()/Remove()/Insert()– NOT if a property of a sub-item changes!

• Implements INotifyCollectionChanged

11

private ObservableCollection<Subscription> newsletterSubscriptions;public ObservableCollection<Subscription> NewsletterSubscriptions{ get { return newsletterSubscriptions; } set {

newsletterSubscriptions = value; OnPropertyChanged(“NewsletterSubscriptions"); //??? Just skip the setter }}

V 1.0 ÓE-NIK, 2014

Állapotértesítés – ObservableCollection<T>

12

List<Subscription> uj = new List<Subscription>();uj.Add(new Subscription("gyereknevelés", true));currentPerson.NewsletterSubscriptions = uj; //setter called event called currentPerson.NewsletterSubscriptions[0] = new Subscription("gyereknevelés", true); //listbox refreshedcurrentPerson.NewsletterSubscriptions.Add( new Subscription("gyereknevelés", true)); //listbox refreshed

currentPerson.NewsletterSubscriptions[0].Active = false; //nothing happens

V 1.0 ÓE-NIK, 2014

Notifications – Problems• ToString() outputs will never refresh/change…

– In WPF, we rather avoid relying on ToString() only!– Instead: use a content manager (e.g. Grid) in the Content

property, and then use separate UI elements for the different properties, and use Data Binding for each of them

– Use DataTemplates if there are many UI elements with same content arrangments

– If we use ListBox UI elements, then use DataTemplates or use a self-made user control…

13

V 1.0 ÓE-NIK, 2014

Notifications – Problems• Changes in a property of a property

– Assign the main property to the DataContext of a content manager or a DataTemplate; then assign the sub-properties to the UI elements inside

– Or use complex Path: {Binding Path=Animal.IsDangerous}– Very often: create a customized the user control …

• Changes in a property of an item in a collection– Usually DataTemplate, customized properties…– If we want to detect it in the code, use BindingList<T>

• For every solution, we have to:– Implement the INotifyPropertyChanged interface in EVERY

class that participates in the data binding– Use ObservableCollection<T> instead of the other

collections14

V 1.0 ÓE-NIK, 2014

Notifications – Changes in a property of a property

• The DataContext of the Window is a Person instance

15

<Label Content="{Binding Name}"/><Grid DataContext="{Binding Animal}"> <Label Content="{Binding Name}"/> <Label Content="{Binding IsDangerous}"/></Grid>

class Person : INotifyPropertyChanged { … private Animal animal; public Allat Animal { get { return animal; } set { animal = value; OnPropertyChanged(“Animal"); } }}

V 1.0 ÓE-NIK, 2014

Notification – Detect changes in a property of sub-items

16

class Person : INotifyPropertyChanged{ … private ObservableCollection<Subscriptions> newsletterSubscriptions; public ObservableCollection<Subscriptions> NewsletterSubscriptions { get { return newsletterSubscriptions; } set {

newsletterSubscriptions = value; OnPropertyChanged(“NewsletterSubscriptions"); } } //+the Subscription class also implements} //the INotifyPropertyChanged interface<ListBox ItemsSource="{Binding NewsletterSubscriptions}"> <ListBox.ItemTemplate> <DataTemplate> <StackPanel> <Label Content="{Binding Topic}"/> <Label Content="{Binding Active}"/> </StackPanel> </DataTemplate> </ListBox.ItemTemplate></ListBox>

ItemsControl ItemTemplate, ContentControl ContentTemplate, Grid CellTemplate

V 1.0 ÓE-NIK, 2014

Data Formatters, conversions• Sometimes we do not want the source property’s

value to be used directly, many times we want to change the look or the type of the source value

• Formatters:– Easiest: …StringFormat properties

• ContentControl: ContentStringFormat (only if the Content is a string)• ItemsControl: ItemStringFormat (only if there is no complex DataTemplate)• When using data binding, IF the target type is string (e.g. TextBox.Text), then

we can use the StringFormat inside the {Binding}– Usually it is similar to string.Format

17

<ListBox.ItemTemplate> <DataTemplate> <StackPanel> <Label ContentStringFormat=“Newsletter topic: {0}" Content="{Binding Topic}"/> <Label ContentStringFormat=“Subscription active: {0}" Content="{Binding Active}"/> </StackPanel> </DataTemplate></ListBox.ItemTemplate>

V 1.0 ÓE-NIK, 2014

Data Formatters, conversions• Data conversions:

– Very often it is automatic!• E.g.: BorderBrush from the string elements inside the ListBox

– If we want a custom converter, we have to create a class that implements the IValueConverter interface (System.Windows.Data)

– Two methods: Convert(), ConvertBack()• Convert(): source target• ConvertBack(): target source (not always used)• The Convert() method is automatically called after a ConvertBack() – due to

the change that happens in the source value.18

<ListBox x:Name="listBoxColors" BorderBrush="{Binding ElementName=listBoxColors, Path=SelectedItem}” .../>

V 1.0 ÓE-NIK, 2014

Data Formatters, conversions• Person class:

– Name (string)– Age (int)

• Binding: person.Age label.Content

19

<Window ... xmlns:current="clr-namespace:WpfApplication9” ... > <Grid> <Grid.Resources> <current:AgeConverter x:Key=“AgeConverter"/> </Grid.Resources> <Label Content="Személy neve:" .../> <Label Content="Személy kora kb.:" .../> <Label Content="{Binding Name}” .../> <Label Content="{Binding Age, Converter={StaticResource AgeConverter}}” ... /> </Grid></Window>

Import the .NET namespace

Import the converter class

Assign the converter to the Binding

V 1.0 ÓE-NIK, 2014

Data Formatters, conversions• Converter class

– Since the target is a Label, we do not need the ConvertBack

20

class AgeConverter : IValueConverter { public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { int age = (int)value; if (age < 18) return “child"; else if (age < 30) return “young"; else if (age < 50) return “middle aged"; else if (age < 65) return “getting old"; else return “old"; }

public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { throw new NotImplementedException(); } }

V 1.0 ÓE-NIK, 2014

Resources• Resources are objects that we can use/reuse in various

places of our application• Types:

– object resources: created in the XAML (window/control xaml OR app.xaml)

– Binary resources: images, icons, tables, etc• Every resource will be imported into the resource

dictionary using a unique name (x:Key)– Resources property (in FrameworkElement descendants)

• Sub-elements will receive the resources of their conatiners• Use in XAML or Application.Current.Resources[“resource_name"]

21

<Window ... <Grid> <Grid.Resources> <current:AgeConverter x:Key=“AgeConverter"/> </Grid.Resources> ... </Grid></Window>

This can be accessed in the grid as a static resource

V 1.0 ÓE-NIK, 2014

Resources• We can use object resources in other places:

– Brushes, colors, styles, Data Template elements, arrays, etc… – E.g. usage of a brush:

• Types:– StaticResource: is loaded once when the XAML is loaded, will not change– DynamicResource: can be loaded and changed dynamically

22

<Window ...> <Grid> <Grid.Resources> <SolidColorBrush x:Key=“MyColor" Color="Azure"/> </Grid.Resources> ... <Label Background="{StaticResource ResourceKey=MyColor}" Content="Szép színű" .../> <ListBox Background="{StaticResource ResourceKey=MyColor}" .../> </Grid></Window>

V 1.0 ÓE-NIK, 2014

<Image Source="LockScreen___0600_0337.jpg" />

Resources• Binary resources:

– Any file can be added to the project that the application might need

– We have to change the file’s properties in the solution explorer to allow their usage as a resource (Solution Explorer, Properties window – Build Action)

• Resource: the compiler will add the resource into the .NET assembly• Content: the file will be used separately – it can be changed or deleted

• In the caml, we can reference them using the file name

23

V 1.0 ÓE-NIK, 2014

Exercise – hair saloon

24