This article has been localized into Vietnamese by the community.
Đáp ứng các thay đổi trong data binding
Cho đến nay trong hướng dẫn này, chúng tôi chủ yếu tạo các ràng buộc giữa các thành phần UI và các lớp hiện có, nhưng trong các ứng dụng thực tế, rõ ràng bạn sẽ ràng buộc với các đối tượng dữ liệu của riêng bạn. Điều này thật dễ dàng, nhưng một khi bạn bắt đầu thực hiện nó, bạn có thể khám phá điều gì đó làm bạn thất vọng: Các thay đổi không được phản ánh tự động, giống như trong các ví dụ trước. Như bạn sẽ tìm hiểu trong bài viết này, bạn chỉ cần thêm một chút công việc để điều này xảy ra, nhưng may mắn thay, WPF làm điều này khá dễ dàng.
Phản hồi khi nguồn dữ liệu có thay đổi
Có hai kịch bản khác nhau mà bạn có thể hoặc không muốn xử lý khi xử lý các thay đổi nguồn dữ liệu: Thay đổi danh sách các mục và thay đổi trong các thuộc tính ràng buộc trong từng đối tượng dữ liệu. Cách xử lý chúng có thể khác nhau, tùy thuộc vào những gì bạn đang làm và những gì bạn đang muốn thực hiện, nhưng WPF đi kèm với hai giải pháp rất dễ dàng mà bạn có thể sử dụng: Giao diện ObservableCollection và giao diện INotifyPropertyChanged.
Ví dụ sau đây sẽ cho bạn thấy lý do tại sao chúng ta cần hai điều sau:
<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; }
}
}
Hãy thử tự chạy nó và xem cách bạn thêm một cái gì đó vào danh sách hoặc thay đổi tên của một trong những người dùng, không có gì trong UI được cập nhật. Ví dụ này khá đơn giản, với lớp Người dùng sẽ giữ tên người dùng, ListBox để hiển thị chúng và một số nút để thao tác cả danh sách và nội dung của nó. ItemSource của danh sách được gán cho một danh sách nhanh gồm một vài người dùng mà chúng ta tạo trong hàm tạo của cửa sổ. Vấn đề là không có nút nào có vẻ hoạt động. Hãy sửa nó, trong hai bước dễ dàng.
Phản ánh những thay đổi trong danh sách dữ liệu nguồn
Bước đầu tiên là để UI phản hồi các thay đổi trong nguồn danh sách (ItemSource), như khi chúng ta thêm hoặc xóa người dùng. Những gì chúng ta cần là một danh sách thông báo bất kỳ đích thay đổi nào cho nội dung của nó và may mắn thay, WPF cung cấp một loại danh sách sẽ làm việc đó. Nó được gọi là ObservableCollection và bạn sử dụng nó giống như một List<T>, chỉ có một vài khác biệt.
Trong ví dụ cuối cùng, mà bạn sẽ tìm thấy bên dưới, chúng tôi chỉ đơn giản là thay thế List<User> bằng ObservableCollection<User> - đó là tất cả những gì bạn cần! Điều này sẽ làm cho nút Thêm và Xóa hoạt động, nhưng nó sẽ không làm gì cho nút "Change name", vì thay đổi sẽ xảy ra trên chính đối tượng dữ liệu bị ràng buộc chứ không phải danh sách nguồn - mặc dù bước thứ hai sẽ xử lý tình huống đó.
Phản ánh những thay đổi trong các đối tượng dữ liệu
Bước thứ hai là để cho lớp User tùy chỉnh khi chúng ta triển khai giao diện INotifyPropertyChanged. Bằng cách đó, các đối tượng User của chúng ta có khả năng cảnh báo lớp UI thay đổi đối với các thuộc tính của nó. Điều này hơi phức tạp hơn một chút so với việc chỉ thay đổi loại danh sách, như chúng ta đã làm ở trên, nhưng đây vẫn là một trong những cách đơn giản nhất để thực hiện các cập nhật tự động này.
Đoạn code hoàn chỉnh và thực thi chương trình
Với hai thay đổi được mô tả ở trên, giờ đây chúng ta có một ví dụ SẼ phản ánh các thay đổi trong nguồn dữ liệu. Nó trông như thế này:
<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));
}
}
}
Tổng kết
Như bạn có thể thấy, việc triển khai INotifyPropertyChanged khá dễ dàng, nhưng nó làm cho đoạn code của bạn trở nên rối rắm hơn. Đây là cái giá mà bạn sẽ phải trả. Rõ ràng là bạn chỉ phải gọi NotifyPropertyChanged trong setter's của các thuộc tính mà bạn liên kết - phần còn lại có thể giữ nguyên như cũ.
Mặt khác, ObservableCollection rất dễ xử lý - nó chỉ yêu cầu bạn sử dụng trong những trường hợp bạn muốn các thay đổi đối với danh sách nguồn được phản ánh trong một đích ràng buộc.