WPF – Read-Only và Attached Dependency Property

Trong bài trước tôi đã hướng dẫn cách tạo một Dependency Property đơn giản, tiếp theo chủ đề này là việc tạo Read-Only và Attached Dependency Property. Đây là hai loại Dependency Property được sử dụng cho các mục đích khác nhau, cách tạo chúng cũng có một vài điểm khác biệt so với Dependency Property thông thường.

Read-Only Dependency Property

Các Read-Only Dependency Property dùng để lưu trữ các giá trị không cho phép người dùng thay đổi. Loại property này được dùng để thể hiện trạng thái của đối tượng, ví dụ như property IsMouseOver.

Để đăng kí một read-only dependency property, bạn cần sử dụng phương thức DependencyProperty.RegisterReadonly() với các tham số tương tự như DependencyProperty.Register() nhưng kiểu trả về lại là kiểu DependencyPropertyKey.

Đối tượng DependencyPropertyKey có thể coi như một khóa để bạn truy xuất đến property thông qua thuộc tính DependencyPropertyKey.DependencyProperty.  Đối tượng này cần được khai báo là private hoặc protected để hạn chế truy xuất từ bên ngoài qua phương thức SetValue(DependencyPropertyKey, Object):

private static readonly DependencyPropertyKey AgePropertyKey =
    DependencyProperty.RegisterReadOnly("Age", typeof(int),
                                typeof(Student),
                                new PropertyMetadata(6)
                               );

Sau đó tạo một public field kiểu DependencyProperty, tuy nhiên bạn không thể dùng đối tượng này để làm tham số cho phương thức SetValue(DependencyProperty, Object) vì đây là một read-only property.

public static readonly DependencyProperty AgeProperty = AgePropertyKey.DependencyProperty;

Và cuối cùng là wrapper với private set, như bạn thấy, ta phải dùng phương thức SetValue(DependencyPropertyKey, Object) thay cho SetValue(DependencyProperty, Object):

public int Age
{
    get { return (int)GetValue(AgeProperty); }
    private set { SetValue(AgePropertyKey, value); }
}

Tổng hợp lại ta có lớp Student hoàn chỉnh với một Read-only Dependency Property là Age:

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

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

Thử kiểm tra bằng cách gán giá trị cho property Age hoặc qua phương thức SetValue(), bạn sẽ nhận được một ngoại lệ:

System.InvalidOperationException: ‘Age’ property was registered as read-only and cannot be modified without an authorization key.

Attached Property

Attached property là các property được sử dụng bởi một vài control nhưng thực sự chúng được định nghĩa trong các lớp khác. Các ví dụ về attached property mà bạn có thể thấy là layout control như Grid, Canvas, DockPanel (Xem bài WPF – Cơ bản về Layout).

Ví dụ với bạn có một Button trong DockPanel, bạn có thể gán layout Dock.Top cho Button này bằng cách viết (XAML):

<Button DockPanel.Dock=”Top”>Hello!</Button>

Lớp Button thực tế không chứa property Dock mà được định nghĩa trong DockPanel. Trong code-behind, bạn có thể dùng một trong hai cách viết:

DockPanel.SetDock(button1,Dock.Bottom);

// or

button1.SetValue(DockPanel.DockProperty, Dock.Bottom);

Tạo một attached property cũng tương tự như tạo một dependency property thông thường. Thay vì gọi phương thức DependencyProperty.Register(), bạn phải thay bằng DependencyProperty.RegisterAttached(). Ví dụ DockPanel của WPF định nghĩa một field sau:

public static readonly DependencyProperty DockProperty =
DependencyProperty.RegisterAttached("Dock", typeof(Dock), typeof(DockPanel),
new FrameworkPropertyMetadata(Dock.Left));

Đồng thời thêm hai phương thức SetDock() và GetDock(), ở đây ta không cần tạo một wrapper cho property. Như vậy lớp DockPanel trong WPF được định nghĩa theo dạng sau:

public class DockPanel : Panel
{
    // ...
    public static readonly DependencyProperty DockProperty =
        DependencyProperty.RegisterAttached("Dock", typeof(Dock), typeof(DockPanel),
        new FrameworkPropertyMetadata(Dock.Left));

    public static void SetDock(UIElement element, Dock value)
    {
        element.SetValue(DockProperty, value);
    }

    public static Dock GetDock(UIElement element)
    {
        return (Dock)element.GetValue(DockProperty);
    }
    // ...
}

https://yinyangit.wordpress.com

Advertisements

2 thoughts on “WPF – Read-Only và Attached Dependency Property

  1. Bạn có đưa ví dụ về Readonly Dependency Property, mình có thử ví dụ sau:
    try
    {
    Student sd = new Student();
    sd.Age = 12;
    }
    catch (Exception ex)
    {
    MessageBox.Show(ex.ToString());
    }

    Bạn nói rằng:
    Thử kiểm tra bằng cách gán giá trị cho property Age hoặc qua phương thức SetValue(), bạn sẽ nhận được một ngoại lệ:

    System.InvalidOperationException: ‘Age’ property was registered as read-only and cannot be modified without an authorization key.

    nhưng sao mình ko bị lỗi này nhỉ? Thanks.

    Phản hồi
  2. Mình xin nói thêm 1 chút về Readonly dependency property. Property dạng này không có nghĩa là giá trị của nó không thể thay đổi, tuy nhiên việc thay đổi này bị giới hạn thông qua CLR property wrapper mà bạn đặt.
    Như trong bài giới thiệu về Dependency property thì bạn có thể thấy là có hai cách để gán giá trị cho property: dùng SetValue() và qua property wrapper. Cách tạo trên sẽ vô hiệu hóa cách gán giá trị bằng SetValue(), tuy nhiên do mình bỏ sót đặt modifier cho setter của propery wrapper nên có thể gán giá trị cho property Age (đã bổ sung thêm private).

    Ngoại lệ xảy ra ở đây là khi dùng SetValue() chứ không phải dùng property. Cảm ơn bạn đã giúp mình sửa lỗi.

    Phản hồ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