Monthly Archives: May 2013

Data binding trong Windows Phone 8

(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:

<TextBlock x:Name="songTitle" Text="{Binding Title, Mode=OneWay}" />

C#:

Song song = new Song();
song.Title = "Like A Rose";

txtTitle.DataContext = song;

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:

<Grid x:Name="gridSong" >
      <TextBlock x:Name="txtTitle" Text="{Binding Title, Mode=OneWay}" />
</Grid>

C#:

Song song = new
Song();
song.Title = "Like A Rose";

gridSong.DataContext = song;

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:

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));
		}
	}
}

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:

<phone:LongListSelector x:Name="listSong" ItemsSource="{Binding Songs}">
	<phone:LongListSelector.ItemTemplate>
		<DataTemplate>
			<TextBlock Text="{Binding Title}" />
		</DataTemplate>
	</phone:LongListSelector.ItemTemplate>                
</phone:LongListSelector>

C#:

public class Album
{
	public List Songs { get; set; }

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

:

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;

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#:

public class Album
{
     public ObservableCollection Songs { get; set; }
     public Album() {
          this.Songs = new ObservableCollection();
     }
}

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#:

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 "";
		}
	}
}

XAML:

<phone:PhoneApplicationPage.Resources>
	<Utils:CountryConverter x:Key="CountryConverter"/>
</phone:PhoneApplicationPage.Resources>

Và:

<TextBlock x:Name="txtCountry" Text="{Binding Code, Converter={StaticResource CountryConverter}}" />

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

Thợ lành nghề #12: Ba dòng mã xấu xí (SMCRemote – phần 2)

Tác giả: Robert C. Martin Người dịch: Hoàng Ngọc Diêu | Biên tập: Phạm Anh Đới Ngày 18 Tháng 3 năm 2003 Tôi nghỉ giải lao trên đài quan sát. Khi lớp chắn bằng nước đá đi xuyên qua vùng phân … read more

Thợ lành nghề #11: Dùng hàm main để làm gì? (SMCRemote – phần 1)

Tác giả: Robert C. Martin Người dịch: Hoàng Ngọc Diêu | Biên tập: Phạm Anh Đới Bạn có thể tải mã nguồn của bài viết trước ở đây. Ngày 18 tháng 2 năm 2003 Trong đầu tôi cứ cân nhắc mãi mớ … read more

Thợ lành nghề #10: Những thread lửng lơ (Vòng lặp không hạn chế)

Tác giả: Robert C. Martin Người dịch: Hoàng Ngọc Diêu | Biên tập: Phạm Anh Đới Bạn có thể tải mã nguồn của bài viết trước ở đây. Ngày 14 tháng 1 năm 2003. Hàng tháng tôi dùng điểm tâm một … read more

Thợ lành nghề #9: Những thread nguy hiểm (Dịch vụ Socket 4)

Tác giả: Robert C. Martin Người dịch: Hoàng Ngọc Diêu | Biên tập: Phạm Anh Đới Câu chuyện tay học việc trẻ tuổi của chúng ta học được bài nằm lòng: Không để các thread đeo lủng lẳng – phải nắm … read more