WPF – Tìm hiểu về Dependency Property

Khác với các property mà bạn thường sử dụng trong .NET (CLR Property), WPF sử dụng một kiểu property mới được gọi là Dependency Property. Trong bài tôi sẽ giới thiệu các kiến thức cơ bản về Dependency Property và cách tạo một Dependency Property đơn giản.

CLR Property và Dependency Property

CLR property được hiện thực bằng cách sử dụng một private field và một “wrapper” (get, set) để lưu trữ và truy xuất giá trị. Trong khi đó, dependency property lưu trữ các giá trị trong một dictionary gồm các dòng dữ liệu dạng key/value, với key là tên của property và value là giá trị lưu trữ.

Lợi ích của dependency property là không phải tốn bộ nhớ lưu trữ các private field mà đa số chúng chỉ mang giá trị mặc định. Dependency property chỉ chỉ lưu trữ các giá trị bị thay đổi. Các giá trị mặc định của kiểu dữ liệu được lấy từ Dependency Property Metadata. Một số giá trị mặc định mặc định của các kiểu dữ liệu khi bạn tạo dependency property:

–          Kiểu tham chiếu (bao gồm string): null

–          Kiểu structure: dựa vào default constructor

–          Kiểu số: zero

Trong trường hợp phần tử hiện tại không chứa giá trị của property cần lấy, dependency property sẽ duyệt lên các phần tử ở mức cao hơn để tìm giá trị (ví dụ Button > Grid > Window).  Ví dụ như khi bạn gán FontSize cho Window, các thành phần con của nó cũng được áp dụng giá trị FontSize này, tính năng này được gọi là Value Inheritance.

Khi một dependency property được đăng ký, bạn có thể chỉ định các phương thức callback xử lý trong trường hợp giá trị của chúng bị thay đổi. Ngoài ra, dependency property liên quan mật thiết đến những tính năng của WPF như resource, style, animation, data binding,….

Sử dụng Dependency Property

Các dependency property có thể được truy xuất trực tiếp thông qua tên của chúng trong tài liệu XAML. Trong code-behind, chúng được truy xuất thông qua hai phương thức GetValue() và SetValue(). Để thuận tiện, bạn nên tạo một wrapper giống như CLR property.

Trong XAML:

Với kiểu dữ liệu đơn giản, bạn có thể gán trực tiếp như các attribute của thẻ, như property Width sau:

<Button Width=”100″>Button1</Button>

Tuy nhiên bạn cũng có thể dùng cú pháp khác, sử dụng các thẻ lồng nhau:

<Button Content=”Button1″>
        <Button.Width>100</Button.Width>
</Button>

Cú pháp trên chỉ thường được dùng cho những property có kiểu dữ liệu phức tạp, ví dụ với property Grid.RowDefinitions:

<Grid>
    <Grid.RowDefinitions>
        <RowDefinition Height="50" />
        <RowDefinition Height="*" />
    </Grid.RowDefinitions>
...
</Grid>

Trong Code-Behind:

Bởi vì các thành phần WPF đã tạo sẵn các wrapper (get/set), ta sử dụng như các CLR property thông thường:

double width = button1.Width;
button1.Width = 100;

Bạn cũng có thể sử dụng hai phương thức GetValue(DependencyProperty) và SetValue(DependencyProperty, value), tuy nhiên nên sử dụng cách trên:

double width = (double)button1.GetValue(Button.WidthProperty);
button1.SetValue(Button.WidthProperty,100d);

Tạo Dependency Property

Để tạo một dependency property, class của bạn phải kế thừa từ DependencyObject. Sau đó bạn tạo một static field kiểu DependencyProperty và khởi tạo nó bằng cách gọi phương thức DependencyProperty.Register().

Phương thức static dùng để đăng kí một property vào dictionary, gồm 3 overload và có kiểu trả về là DependencyProperty. Overload đầy đủ nhất có dạng (C#):

public static DependencyProperty Register(
         string name,
         Type propertyType,
         Type ownerType,
         PropertyMetadata typeMetadata,
         ValidateValueCallback validateValueCallback
)

Trong đó:

–          name: tên của dependency property sẽ đăng kí, theo tiêu chuẩn đặt tên của .NET thì bạn nên thêm hậu tố là Property (ví dụ WidthProperty).

–          propertyType: kiểu dữ liệu của property.

–          ownerType: kiểu dữ liệu chứa property (đăng kí property)

–          typeMetadata: giá trị của property (Xem: System.Windows.PropertyMetadata)

–          validateValueCallback: delegate của phương thức dùng để kiểm tra tính hợp lệ của dữ liệu mỗi khi được gán cho property. Delegate này có dạng:

public delegate bool ValidateValueCallback(
     Object value
)

Xem: System.Windows.ValidateValueCallback

Như vậy ta có thể tạo class Student gồm một dependency property Age cơ bản như sau, với giá trị ban đầu của Age là 6:

public class Student: DependencyObject
{
    public static readonly DependencyProperty AgeProperty =
        DependencyProperty.Register("Age", typeof(int),
                                    typeof(Student),
                                    new PropertyMetadata(6)
                                   );

    // CLR Property Wrapper
    public int Age
    {
        get { return (int)GetValue(AgeProperty); }
        set { SetValue(AgeProperty, value); }
    }

}

Kiểm tra hợp lệ cho Property (Property Validation)

Value Changed Callback và Coerce Value Callback

Contructor của PropertyMetadata bao gồm overload cho phép truyền vào các delegate callback dùng để kích hoạt các phương thức quản lý dữ liệu được gán cho property. Hai loại callback này gồm:

–          Value Changed Callback: kích hoạt khi giá trị của property bị thay đổi.

–          Coerce Value Callback: kích hoạt khi phương thức DependencyObject.CoerceValue() được gọi (tự động) để “ép” giá trị của property về giá trị hợp lệ nếu nó nằm ngoài vùng dữ liệu cho phép.

Như vậy ta có thể sửa lại ví dụ trên như sau:

public static readonly DependencyProperty AgeProperty =
    DependencyProperty.Register("Age", typeof(int),
                                typeof(Student),
                                new PropertyMetadata(6,AgeChangedCallback, AgeCoerceCallback)
                               );

private static void AgeChangedCallback(
    DependencyObject d, DependencyPropertyChangedEventArgs e)
{
    // do something
}

private static object AgeCoerceCallback(DependencyObject d, object baseValue)
{
    int age = (int)baseValue;

    if (age < 6)
        age = 6;
    else if(age>50)
        age = 50;

    return age;
}

Validate Value Callback

Đây là loại callback được dùng để kiểm tra tính hợp lệ của dữ liệu gán cho property. Phương thức callback này sẽ kiểm tra trước khi Coerce Value Callback được gọi. Giá trị trả về của Validation callback là kiểu boolean với giá trị true tương ứng với dữ liệu hợp lệ. Khi dữ liệu được kiểm tra là không hợp lệ, callback này sẽ ném ra một ngoại lệ ArgumentException.

private static bool AgeValidateCallback(object value)
{
    if(value is int)
    {
        int age = (int)value;
        return age >= 0;
    }
    return false;
}

Mã nguồn hoàn chỉnh của lớp Student

Tạo một project WPF và thêm vào lớp Studen.cs.

Student.cs:

public class Student: DependencyObject
{
    public static readonly DependencyProperty AgeProperty =
        DependencyProperty.Register("Age", typeof(int),
                                    typeof(Student),
                                    new PropertyMetadata(6,AgeChangedCallback, AgeCoerceCallback),
                                    AgeValidateCallback
                                   );

    // CLR Property Wrapper
    public int Age
    {
        get { return (int)GetValue(AgeProperty); }
        set { SetValue(AgeProperty, value); }
    }

    private static void AgeChangedCallback(
        DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        // do something
    }

    private static object AgeCoerceCallback(DependencyObject d, object baseValue)
    {
        int age = (int)baseValue;

        if (age < 6)
            age = 6;
        else if(age>50)
            age = 50;

        return age;
    }
    private static bool AgeValidateCallback(object value)
    {
        if(value is int)
        {
            int age = (int)value;
            return age >= 0;
        }
        return false;
    }
}

Trong tập tin Window1.xaml.cs, bạn sửa lại lớp Window1 như sau:

public partial class Window1 : Window
{
    public Window1()
    {
        InitializeComponent();

        Student st=new Student();
        try
        {
            st.Age=4;
            MessageBox.Show(st.Age.ToString());     // Output: 6 (Coerce Value Callback)

            st.Age=-1; // ArgumentException (Validate Value Callback)
        }
        catch(Exception ex)
        {
            MessageBox.Show(ex.ToString());
        }
    }

}

https://yinyangit.wordpress.com

Advertisements

9 thoughts on “WPF – Tìm hiểu về Dependency Property

  1. Pingback: MVVM trong WPF (Part 3/4): Sử dụng RelayCommand cho các loại control « Nguyễn Ngọc Vạn's Blog

  2. Em làm silverlight dùng thủ tục để load lên datagrid từ 2 bảng trong csdl
    PhongBan(TenPB, MaPB)
    NhanVien(TenNV,QueQuan,MaNV)
    em làm bên phần nhập nhân viên
    Tên NV, MaPB, QueQuan,MaNV
    khi em dùng thủ tục thì khi nhập vào thì nó chưa load lên datagrid mà phải tắt đi bật lại mới hiển thị
    thủ tục em select từ 2 bảng
    Em muốn hỏi anh là khi em nhập vào thì dữ liệu được hiển thị lên datagrid luôn,
    nếu được anh sửa trên VD em gửi. em đã làm nhưng ko hiển thị ngay lên được.
    Mong anh giúp em.

    Trả lời

Trả lời

Mời bạn điền thông tin vào ô dưới đây hoặc kích vào một biểu tượng để đăng nhập:

WordPress.com Logo

Bạn đang bình luận bằng tài khoản WordPress.com Log Out / Thay đổi )

Twitter picture

Bạn đang bình luận bằng tài khoản Twitter Log Out / Thay đổi )

Facebook photo

Bạn đang bình luận bằng tài khoản Facebook Log Out / Thay đổi )

Google+ photo

Bạn đang bình luận bằng tài khoản Google+ Log Out / Thay đổi )

Connecting to %s