This article is currently in the process of being translated into Croatian (~99% done).
Responding to changes
U ovom smo vodiču do sada uglavnom kreirali poveznice između elemenata grafičkog sučelja i postojećih klasa, ali u stvarnim aplikacijama vjerojatno ćete ih povezivati s vlastitim podatkovnim objektima. To je jednako jednostavno, ali jednom kada krenete s tim, možda otkrijete nešto što će vas razočarati: Promjene se ne odražavaju automatski kao što je to bilo u ranijim primjerima. Kao što ćete naučiti u ovom članku, potrebno je malo dodatnog posla da bi do toga došlo, no srećom WPF ovo čini poprilično jednostavnim.
Odgovor na promjene u izvoru podataka
Postoje dva različita scenarija koja možda želite ili ne želite obraditi kada se bavite promjenama izvora podataka: Promjene u popisu stavki i promjene u vezanim svojstvima svakog od podatkovnih objekata. Način obrade može varirati, ovisno o tome što radite i što želite postići, ali WPF dolazi s dva vrlo jednostavna rješenja koja možete koristiti: ObservableCollection i INotifyPropertyChanged sučelje.
Sljedeći primjer će vam pokazati zašto su vam te dvije stvari potrebne:
<Window x:Class="WpfTutorialSamples.DataBinding.ChangeNotificationSample"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="ChangeNotificationSample" Height="150" Width="300">
<DockPanel Margin="10">
<StackPanel DockPanel.Dock="Right" Margin="10,0,0,0">
<Button Name="btnAddUser" Click="btnAddUser_Click">Add user</Button>
<Button Name="btnChangeUser" Click="btnChangeUser_Click" Margin="0,5">Change user</Button>
<Button Name="btnDeleteUser" Click="btnDeleteUser_Click">Delete user</Button>
</StackPanel>
<ListBox Name="lbUsers" DisplayMemberPath="Name"></ListBox>
</DockPanel>
</Window>
using System;
using System.Collections.Generic;
using System.Windows;
namespace WpfTutorialSamples.DataBinding
{
public partial class ChangeNotificationSample : Window
{
private List<User> users = new List<User>();
public ChangeNotificationSample()
{
InitializeComponent();
users.Add(new User() { Name = "John Doe" });
users.Add(new User() { Name = "Jane Doe" });
lbUsers.ItemsSource = users;
}
private void btnAddUser_Click(object sender, RoutedEventArgs e)
{
users.Add(new User() { Name = "New user" });
}
private void btnChangeUser_Click(object sender, RoutedEventArgs e)
{
if(lbUsers.SelectedItem != null)
(lbUsers.SelectedItem as User).Name = "Random Name";
}
private void btnDeleteUser_Click(object sender, RoutedEventArgs e)
{
if(lbUsers.SelectedItem != null)
users.Remove(lbUsers.SelectedItem as User);
}
}
public class User
{
public string Name { get; set; }
}
}
Pokušajte sami pokrenuti ovaj kod i vidjeti da kako da iako dodate nešto u listu ili promijenite ime nekog od korisnika, ništa se ne ažurira na grafičkom sučelju. Ovo je poprilično jednostavan primjer, u kojem User klasa sadrži ime korisnika, ListBox ih prikazuje, a dugmad je tu kako bi manipulirali i listom i njenim sadržajem. ItemsSourceu liste je dodijeljena lista u kojem se nalazi nekoliko korisnika koje smo kreirali kroz konstruktor prozora. Problem je taj što čini se da dugmad ne rade ono što bi trebala. Popravimo to kroz dva jednostavna koraka.
Odražavanje promjena na izvor liste
Prvi korak je dobiti da grafičko sučelje reagira na promjene u izvoru liste (ItemsSource), primjerice kada dodajemo ili brišemo korisnika. Ono što nam treba je lista koja obavještava ostale o promjenama u svom sadržaju i, srećom, WPF pruža vrstu liste koja će upravo to raditi. Naziva se ObservableCollection i koristite ju uglavnom kao regularnu List<T> sa samo jednom razlikom.
U krajnjem primjeru, kojeg možete naći ispod, jednostavno smo zamijenili List<User> s ObservableCollection<User> - to je sve što je potrebno! Zbog ove promjene će Add i Delete dugmad proraditi, ali neće napraviti ništa po pitanju "Change name" dugmeta jer će se promjena dogoditi na povezanom objektu, a ne na samoj izvorišnoj listi - u drugom koraku ćemo obraditi taj scenarij.
Odražavanje promjena u podatkovnim objektima
Drugi korak je da naša User klasa implementira INotifyPropertyChanged sučelje. Na taj način će naši User objekti moći obavijestiti sloj grafičkog sučelja da je došlo do neke promjene u njihovim svojstvima. Ovo je malo nezgodnije od samog mijenjanja liste, kao što smo učinili gore, ali je i dalje jedan od najjednostavnijih načina za postizanje ovih automatskih ažuriranja.
Konačni primjer koji radi
S dvjema opisanim promjenama, sada imamo primjer koji će prikazati promjene u izvorima podataka. Izgleda ovako:
<Window x:Class="WpfTutorialSamples.DataBinding.ChangeNotificationSample"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="ChangeNotificationSample" Height="135" Width="300">
<DockPanel Margin="10">
<StackPanel DockPanel.Dock="Right" Margin="10,0,0,0">
<Button Name="btnAddUser" Click="btnAddUser_Click">Add user</Button>
<Button Name="btnChangeUser" Click="btnChangeUser_Click" Margin="0,5">Change user</Button>
<Button Name="btnDeleteUser" Click="btnDeleteUser_Click">Delete user</Button>
</StackPanel>
<ListBox Name="lbUsers" DisplayMemberPath="Name"></ListBox>
</DockPanel>
</Window>
using System;
using System.Collections.Generic;
using System.Windows;
using System.ComponentModel;
using System.Collections.ObjectModel;
namespace WpfTutorialSamples.DataBinding
{
public partial class ChangeNotificationSample : Window
{
private ObservableCollection<User> users = new ObservableCollection<User>();
public ChangeNotificationSample()
{
InitializeComponent();
users.Add(new User() { Name = "John Doe" });
users.Add(new User() { Name = "Jane Doe" });
lbUsers.ItemsSource = users;
}
private void btnAddUser_Click(object sender, RoutedEventArgs e)
{
users.Add(new User() { Name = "New user" });
}
private void btnChangeUser_Click(object sender, RoutedEventArgs e)
{
if(lbUsers.SelectedItem != null)
(lbUsers.SelectedItem as User).Name = "Random Name";
}
private void btnDeleteUser_Click(object sender, RoutedEventArgs e)
{
if(lbUsers.SelectedItem != null)
users.Remove(lbUsers.SelectedItem as User);
}
}
public class User : INotifyPropertyChanged
{
private string name;
public string Name {
get { return this.name; }
set
{
if(this.name != value)
{
this.name = value;
this.NotifyPropertyChanged("Name");
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
public void NotifyPropertyChanged(string propName)
{
if(this.PropertyChanged != null)
this.PropertyChanged(this, new PropertyChangedEventArgs(propName));
}
}
}
Sažetak
Kao što možete vidjeti, implementacija INotifyPropertyChanged je poprilično jednostavna, ali je potreban dodatan kod u vašim klasama i potrebno je dodati nešto logike vašim svojstvima. To je cijena koju morate platiti ako želite povezati vlastite klase i ako želite da se izmjene odmah odraze na korisničko sučelje. Očito trebate pozvati NotifyPropertyChanged samo u setterima svojstava na koja se vežete - ostatak može ostati kakav jest
U drugu ruku, s ObservableCollection je jednostavno raditi - samo zahtjeva da koristite specifičnu listu u onim situacijama kada želite da se promjena na izvoru odrazi na povezano odredište.