This article has been localized into Czech by the community.
Reagování na změny
Doposud jsme v tomto výukovém kurzu většinou vytvořili vazby mezi prvky uživatelského rozhraní a existujícími třídami, ale v reálných aplikacích budete chtít zcela určitě vázat data k vlastním datovým objektům. Jde to stejně snadno, ale jakmile to uděláte, možná objevíte něco, co vás vyvede z míry: změny se automaticky neprojeví, tak jako v předchozích příkladech. Jak se naučíte v tomto článku, chce to jen trochu práce navíc, aby to začalo fungovat. Ale naštěstí je to ve WPF velmi snadné.
Reagování na změny ve zdroji dat
Existují dva různé scénáře, které se mohou, ale nemusí při práci se s vazbami dat hodit: Změny v seznamu položek a změny ve vlastnostech vazby v jednotlivých objektech dat. Způsob jejich zpracování se může lišit v závislosti na tom, co děláte a čeho chcete dosáhnout. Nicméně WPF nabízí dvě velmi jednoduchá řešení, která můžete použít: kolekci ObservableCollection a a rozhraní INotifyPropertyChanged
Následující příklad vám ukáže, proč jsou tyto dvě věci potřeba:
<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; }
}
}
Spusťte si tento příklad a podívejte se, že ačkoliv se snažíte do seznamu cokoliv přidat, nebo se snažíte změnit jméno některého z uživatelů, nic se v uživatelském rozhraní neaktualizuje. Příklad je docela jednoduchý, obsahuje třídu User, která bude uchovávat jméno uživatele, které se zobrazí v ListBox, a několik tlačítek pro manipulaci se seznamem a jeho obsahem. Položka ItemsSource seznamu je přiřazena k seznamu několika uživatelů, který vytvoříme v konstruktoru okna. Problém je v tom, že žádné z tlačítek nefunguje. To napravíme dvěma snadnými kroky.
Promítnutí změn do zdroje dat seznamu
Prvním krokem je získání odezvy uživatelského rozhraní na změny ve zdroji dat (ItemsSource), například při přidání nebo odstranění uživatele. Potřebujeme seznam, který by své okolí informoval o všech změnách svého obsahu. WPF takový typ seznamu naštěstí poskytuje. Jmenuje se ObservableCollection a používá se podobně jako běžný List < T > s několika rozdíly.
V posledním příkladu, který najdete níže, jsme jednoduše nahradili List<User> za ObservableCollection<User> - to je vše! Tím jsou funkční tlačítka Přidat a Odstranit, ale tlačítko "Změnit jméno" není funkční, protože změna se provede na samotném vázaném datovém objektu, a ne na zdrojovém seznamu - druhý krok však tento scénář vyřeší.
Promítnutí změn do datových objektů.
Druhým krokem je nechat naši vlastní třídu User implementovat rozhraní INotifyPropertyChanged. Tímto způsobem mohou naše objekty ve třídě User upozornit uživatelské rozhraní na změny svých vlastností. Jedná se o poněkud složitější postup než jenom změnit typu seznamu, jak jsme to udělali výše, ale stále je to jeden z nejjednodušších způsobů, jak tyto automatické aktualizace provést.
Závěrečný a fungující příklad
Se dvěma výše popsanými změnami máme nyní příklad, který zajistí změny ve zdroji dat. Vypadá takto:
<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));
}
}
}
Shrnutí
Jak můžete vidět, implementace INotifyPropertyChanged je docela snadná, ale ve vašich třídách vyžaduje trochu více kódu a k vašim vlastnostem přidání trochu extra logiky. Je to cena, kterou budete muset zaplatit, pokud chcete vytvářet vazby na vlastní třídy a okamžitě promítat změny v uživatelském rozhraní. Zřejmě budete muset volat NotifyPropertyChanged pouze ve vlastnostech, které vážete, a zbytek může zůstat tak, jak je.
Na druhou stranu je velmi snadné pracovat s kolekcí ObservableCollection, pouze to vyžaduje použit tento specifický typu seznamu v situacích, kdy chcete, aby se změny ve zdrojovém seznamu projevily ve vázaném objektu.