(Tạp chí Lập trình) – Data binding (liên kết dữ liệu) là một trong những cách đơn giản để ứng dụng của bạn hiển thị và tương tác với dữ liệu. Việc tách riêng phần giao diện (UI) và dữ liệu (data) sẽ mang lại sự thuận tiện khi thiết kế giao diện cũng như là khi viết mã quản lý dữ liệu. Khi có một kết nối (connection) giữa phần giao diện với phần dữ liệu thì những thay đổi từ một phía sẽ được cập nhật ở phía còn lại. Ví dụ, bạn dùng một TextBox để hiển thị tên của người dùng, TextBox này được liên kết (bind) với thuộc tính Name của một đối tượng người dùng, nếu bạn nhập một tên khác vào TextBox thì giá trị này sẽ được cập nhật tự động vào thuộc tính Name. Tương tự như vậy, nếu bạn thay đổi giá trị của Name thì giá trị này sẽ cập nhật lên TextBox.

Data binding được sử dụng ở trong rất nhiều trường hợp: khi bạn muốn hiển thị danh sách các bài hát với ListBox (hoặc LongListSelector), hiển thị tên người dùng với TextBlock, hiển thị ảnh với Image v.v. Trong bài viết này chúng ta sẽ dùng những ví dụ nhỏ để minh họa cho các trường hợp bạn hay sử dụng Data binding.

Kết nối Giao diện với Dữ liệu

Để tạo một liên kết thì bạn cần có đối tượng nguồn (Binding Source) và đối tượng đích (Binding Target), hãy thử quan sát hình sau:

Binding Source có thể là bất cứ một đối tượng nào, nó chứa dữ liệu mà sẽ được liên kết đến Binding Target.

Để có thể trở thành Binding Target thì đối tượng giao diện phải là một FrameworkElement, còn thuộc tính của nó phải là một DependencyProperty. Ví dụ, TextBlock là một đối tượng của FrameworkElement còn thuộc tính Text của nó là một DependencyProperty.

Trong một liên kết thì chúng ta có thể xác định các thuộc tính cơ bản sau:

  • Đối tượng nguồn (Binding Source), đối tượng đích (Binding Target).
  • Chiều của dữ liệu. Việc này được thực hiện thông qua thuộc tính Binding.Mode.
  • Đối tượng converter. Đây là đối tượng được sử dụng để thực hiện việc chuyển đổi giá trị của Binding Source sang một giá trị khác được sử dụng cho Binding Target và ngược lại. Một converter phải là một đối tượng của IValueConverter.

Ngoài ra thì còn có một số thuộc tính khác, được mô tả chi tiết trong lớp Binding.

Ví dụ, lớp Song có thuộc tính là Title, và bây giờ chúng ta muốn hiển thị tên của bài hát trên một TextBlock thì sẽ làm như sau:

XAML:

[source lang=”xml”]
<TextBlock x:Name="songTitle" Text="{Binding Title, Mode=OneWay}" />
[/source]

C#:

[source lang=”csharp”]
Song song = new Song();
song.Title = "Like A Rose";

txtTitle.DataContext = song;
[/source]

Việc tạo liên kết được thực hiện trong file XAML bằng cách sử dụng cú pháp =”{Binding}. Sau đó thì nguồn dữ liệu sẽ được xác định thông qua việc thay đổi giá trị của thuộc tính DataContext của TextBlock.

Giá trị của DataContext sẽ được kế thừa từ các thành phần cha đến các thành phần con. Ví dụ:

XAML:

[source lang=”xml”]
<Grid x:Name="gridSong" >
<TextBlock x:Name="txtTitle" Text="{Binding Title, Mode=OneWay}" />
</Grid>
[/source]

C#:

[source lang=”csharp”]
Song song = new
Song();
song.Title = "Like A Rose";

gridSong.DataContext = song;
[/source]

Trong trường hợp này, chúng ta đã gán giá trị cho thuộc tính DataContext của Grid, nhưng giá trị này cũng sẽ được giữ nguyên cho TextBlock. Nếu bạn không muốn sử dụng giá trị được kế thừa từ Grid thì đơn giản chỉ cần gán giá trị khác cho thuộc tính DataContext của nó.

Chiều của liên kết

Có ba giá trị mà chúng ta có thể sử dụng cho thuộc tính Mode:

  • OneTime: Binding Target sẽ được cập nhật một lần duy nhất khi liên kết được khởi tạo.
  • OneWay: Binding Target sẽ được cập nhật khi liên kết được khởi tạo và mỗi khi giá trị của Binding Source được thay đổi.
  • TwoWay: Sự thay đổi giá trị của Binding Source sẽ được cập nhật lên Binding Target và theo chiều ngược lại.

Để việc cập nhật giá trị được thực hiện một cách tự động thì đối tượng Binding Source phải triển khai interface INotifyPropertyChanged.

Interface INotifyPropertyChanged có một event là PropertyChanged, event này sẽ làm nhiệm vụ thông báo cho Binding Target biết để cập nhật mỗi khi có sự thay đổi giá trị ở Binding Source. Hãy quan sát cách sử dụng INotifyPropertyChanged trong lớp Song:

[source lang=”csharp”]
public class Song : INotifyPropertyChanged
{
private string _title;
public String Title
{
get
{
return this._title;
}
set
{
this._title = value;
NotifyPropertyChanged("Title");
}
}

public event PropertyChangedEventHandler PropertyChanged;

public void NotifyPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
[/source]

Mỗi khi giá trị của Title được thay đổi thì event PropertyChanged
sẽ được tung ra và như vậy giá trị mới này sẽ được cập nhật lên TextBlock.

Trong trường hợp này, liên kết của chúng ta là OneWay cho nên dữ liệu chỉ được cập nhật theo hướng từ Binding Source đến Binding Target. Nếu ta thay TextBlock bằng một TextBox đồng thời chuyển Mode của nó thành TwoWay thì khi ta thay đổi giá trị của TextBox, giá trị mới này sẽ được cập nhật ngay cho thuộc tính Title của đối tượng song.

Liên kết với Collection

Thay vì chỉ liên kết với một bài hát, nếu chúng ta muốn hiển thị một danh sách bài hát thì có thể tạo liên kết tới một List.

XAML:

[source lang=”xml”]
<phone:LongListSelector x:Name="listSong" ItemsSource="{Binding Songs}">
<phone:LongListSelector.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Title}" />
</DataTemplate>
</phone:LongListSelector.ItemTemplate>
</phone:LongListSelector>
[/source]

C#:

[source lang=”csharp”]
public class Album
{
public List Songs { get; set; }

public Album() {
this.Songs = new List();
}
}

[/source]

:

[source lang=”csharp”]
Album album = new Album();
album.Songs.Add(new Song {Title = "Like A Rose" });
album.Songs.Add(new Song { Title = "Như Chưa Từng Yêu" });
album.Songs.Add(new Song { Title = "Mùa Yêu Đầu" });
listSong.DataContext = album;
[/source]

Trong trường hợp ta sử dụng List như trên thì nếu ta thêm một đối tượng Song mới, đối tượng này sẽ không được cập nhật lên LongListSelector. Để việc này xảy ra, chúng ta hãy sử dụng ObservableCollection thay cho List:

C#:

[source lang=”csharp”]
public class Album
{
public ObservableCollection Songs { get; set; }
public Album() {
this.Songs = new ObservableCollection();
}
}
[/source]

Chuyển đổi giá trị

Có những trường hợp bạn muốn hiển thị dữ liệu (ở Binding Target) theo một cách khác so với cách mà bạn đang lưu trữ nó (ở Binding Source). Ví dụ, bạn có điểm thi của một môn học nằm trong khoảng 0 – 10 nhưng lại không muốn hiển thị điểm số mà hiển thị phân loại của nó (kém, trung bình, khá, giỏi…), bạn có mã một quốc gia (vn, us, cn…) nhưng lại muốn hiển thị tên của quốc gia đó…,cách thuận tiện nhất để thực hiện việc này đó là sử dụng một đối tượng IValueConverter.

C#:

[source lang=”csharp”]
public class CountryConverter:IValueConverter
{

public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
string code = (string)value;
switch (code) {
case "vn": return "Vietnam";
case "us": return "United State";
case "cn": return "China";
case "jp": return "Japan";
default: return "Other country";
}
}

public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
string country = (string)value;
switch (country)
{
case "Vietnam": return "vn";
case "United State": return "us";
case "China": return "cn";
case "Japan": return "jp";
default: return "";
}
}
}
[/source]

XAML:

[source lang=”xml”]
<phone:PhoneApplicationPage.Resources>
<Utils:CountryConverter x:Key="CountryConverter"/>
</phone:PhoneApplicationPage.Resources>
[/source]

Và:

[source lang=”xml”]
<TextBlock x:Name="txtCountry" Text="{Binding Code, Converter={StaticResource CountryConverter}}" />
[/source]

Khi dữ liệu được chuyển từ Binding Source đến Binding Target thì phương thức Convert() được sử dụng, còn theo chiều ngược lại thì phương thức ConvertBack() sẽ được sử dụng.

Để biết thêm về các chi tiết khi cài đặt Data binding trên Windows Phone, bạn có thể tham khảo thêm các ví dụ rất cụ thể ở đây: http://msdn.microsoft.com/en-us/library/windowsphone/develop/jj207023%28v=vs.105%29.aspx

Nguyễn Khắc Nhật