Sự khác biệt giữa thuộc tính phụ thuộc và thuộc tính đính kèm trong WPF là gì?


91

Sự khác biệt giữa thuộc tính phụ thuộc (tùy chỉnh) và thuộc tính đính kèm trong WPF là gì? Công dụng của từng loại là gì? Cách triển khai thường khác nhau như thế nào?

Câu trả lời:


20

trừu tượng

Vì tôi tìm thấy ít hoặc không có tài liệu về vấn đề này, nên phải mất một số lần dò tìm mã nguồn , nhưng đây là câu trả lời.

Có sự khác biệt giữa việc đăng ký một thuộc tính phụ thuộc như một thuộc tính thông thường và một thuộc tính đính kèm, khác với một thuộc tính "triết học" ( các thuộc tính thông thường được sử dụng cho kiểu khai báo và các kiểu dẫn xuất của nó, các thuộc tính đính kèm được dự định sử dụng như phần mở rộng trên các DependencyObject phiên bản tùy ý ). "Triết học", bởi vì, như @MarqueIV đã nhận thấy trong nhận xét của mình cho câu trả lời của @ ReedCopsey, các thuộc tính thông thường cũng có thể được sử dụng với các DependencyObjecttrường hợp tùy ý .

Hơn nữa, tôi không đồng ý với các câu trả lời khác nói rằng thuộc tính đính kèm là "loại thuộc tính phụ thuộc", bởi vì nó gây hiểu lầm - không có bất kỳ "loại" thuộc tính phụ thuộc nào. Khung công tác không quan tâm đến việc tài sản đó có được đăng ký dưới dạng đính kèm hay không - thậm chí không thể xác định được (theo nghĩa là thông tin này không được ghi lại, vì nó không liên quan). Trên thực tế, tất cả các thuộc tính đều được đăng ký như thể chúng là các thuộc tính đính kèm, nhưng trong trường hợp là thuộc tính thông thường, một số điều bổ sung được thực hiện để sửa đổi một chút hành vi của chúng.

Đoạn mã

Để giúp bạn đỡ rắc rối khi tự mình duyệt qua mã nguồn, đây là phiên bản tóm tắt của những gì sẽ xảy ra.

Khi đăng ký một thuộc tính mà không chỉ định siêu dữ liệu, hãy gọi

DependencyProperty.Register(
    name: "MyProperty",
    propertyType: typeof(object),
    ownerType: typeof(MyClass))

mang lại kết quả chính xác giống như gọi

DependencyProperty.RegisterAttached(
    name: "MyProperty",
    propertyType: typeof(object),
    ownerType: typeof(MyClass))

Tuy nhiên, khi chỉ định siêu dữ liệu, việc gọi

DependencyProperty.Register(
    name: "MyProperty",
    propertyType: typeof(object),
    ownerType: typeof(MyClass),
    typeMetadata: new FrameworkPropertyMetadata
    {
        CoerceValueCallback = CoerceCallback,
        DefaultValue = "default value",
        PropertyChangedCallback = ChangedCallback
    });

tương đương với việc gọi điện

var property = DependencyProperty.RegisterAttached(
    name: "MyProperty",
    propertyType: typeof(object),
    ownerType: typeof(MyClass),
    defaultMetadata: new PropertyMetadata
    {
        DefaultValue = "default value",
    });
property.OverrideMetadata(
    forType: typeof(MyClass),
    typeMetadata: new FrameworkPropertyMetadata
    {
        CoerceValueCallback = CoerceCallback,
        DefaultValue = "default value",
        PropertyChangedCallback = ChangedCallback
    });

Kết luận

Điểm khác biệt chính (và duy nhất) giữa thuộc tính phụ thuộc thông thường và thuộc tính đính kèm là siêu dữ liệu mặc định có sẵn thông qua thuộc tính DependencyProperty.DefaultMetadata . Điều này thậm chí còn được đề cập trong phần Nhận xét :

Đối với các thuộc tính không được đính kèm, loại siêu dữ liệu do thuộc tính này trả về không thể được chuyển sang các loại dẫn xuất của loại PropertyMetadata , ngay cả khi thuộc tính ban đầu được đăng ký với loại siêu dữ liệu dẫn xuất. Nếu bạn muốn siêu dữ liệu đã đăng ký ban đầu bao gồm loại siêu dữ liệu ban đầu có thể có nguồn gốc, hãy gọi GetMetadata (Loại) để thay thế, chuyển kiểu đăng ký ban đầu làm tham số.

Đối với các thuộc tính đính kèm, loại siêu dữ liệu do thuộc tính này trả về sẽ khớp với loại được cung cấp trong phương thức đăng ký RegisterAttached ban đầu .

Điều này có thể nhìn thấy rõ ràng trong mã được cung cấp. Các gợi ý nhỏ cũng được ẩn trong các phương thức đăng ký, tức là đối RegisterAttachedvới tham số siêu dữ liệu được đặt tên defaultMetadata, trong khi đối với Registernó được đặt tên typeMetadata. Đối với các thuộc tính đính kèm, siêu dữ liệu được cung cấp sẽ trở thành siêu dữ liệu mặc định. Tuy nhiên, trong trường hợp thuộc tính thông thường, siêu dữ liệu mặc định luôn là phiên bản mới của PropertyMetadataonly DefaultValueset (từ siêu dữ liệu được cung cấp hoặc tự động). Chỉ cuộc gọi tiếp theo mới OverrideMetadatathực sự sử dụng siêu dữ liệu được cung cấp.

Kết quả

Sự khác biệt thực tế chính là trong trường hợp thuộc tính thông thường, CoerceValueCallbackchỉPropertyChangedCallback áp dụng cho các loại có nguồn gốc từ loại được khai báo là loại chủ sở hữu và đối với các thuộc tính đính kèm, chúng áp dụng cho tất cả các loại. Ví dụ trong trường hợp này:

var d = new DependencyObject();
d.SetValue(SomeClass.SomeProperty, "some value");

tài sản đã đăng ký PropertyChangedCallback sẽ được gọi nếu tài sản được đăng ký là tài sản đính kèm, nhưng sẽ không được gọi nếu nó được đăng ký là tài sản thông thường. Tương tự với CoerceValueCallback.

Sự khác biệt thứ cấp bắt nguồn từ thực tế là OverrideMetadatayêu cầu loại được cung cấp bắt nguồn từ đâu DependencyObject. Trên thực tế, điều đó có nghĩa là kiểu sở hữu cho các thuộc tính thông thường phải bắt nguồn từ DependencyObject, trong khi đối với các thuộc tính đính kèm trong có thể là bất kỳ kiểu nào (bao gồm các lớp tĩnh, cấu trúc, enum, đại biểu, v.v.).

Phần bổ sung

Bên cạnh đề xuất của @ MarqueIV, trong một vài trường hợp, tôi đã gặp ý kiến ​​rằng các thuộc tính thông thường và thuộc tính đính kèm khác nhau về cách chúng có thể được sử dụng trong XAML . Cụ thể, các thuộc tính thông thường yêu cầu cú pháp tên ngầm định trái ngược với cú pháp tên rõ ràng được yêu cầu bởi các thuộc tính đính kèm. Điều này về mặt kỹ thuật là không đúng , mặc dù trong thực tế nó thường là như vậy. Cho rõ ràng:

<!-- Implicit property name -->
<ns:SomeClass SomeProperty="some value" /> 

<!-- Explicit property name -->
<DependencyObject ns:SomeClass.SomeProperty="some value" />

Trong XAML thuần túy , các quy tắc duy nhất điều chỉnh việc sử dụng các cú pháp này như sau:

  • Cú pháp tên ngầm định có thể được sử dụng trên một phần tử nếu và chỉ khi lớp mà phần tử này đại diện có thuộc tính CLR của tên đó
  • Cú pháp tên rõ ràng có thể được sử dụng trên một yếu tố khi và chỉ khi các lớp xác định bởi phần đầu của tên đầy đủ cho thấy tĩnh thích hợp get / thiết lập các phương pháp (gọi tắt là accessors ) với những cái tên phù hợp với phần thứ hai của tên đầy đủ

Việc đáp ứng các điều kiện này cho phép bạn sử dụng cú pháp tương ứng bất kể thuộc tính phụ thuộc sao lưu được đăng ký là thông thường hay được đính kèm.

Hiện nay, quan niệm sai lầm được đề cập là do phần lớn các hướng dẫn (cùng với các đoạn mã Visual Studio có sẵn ) hướng dẫn bạn sử dụng thuộc tính CLR cho các thuộc tính phụ thuộc thông thường và lấy / đặt trình truy cập cho các thuộc tính đính kèm. Nhưng không có gì ngăn cản bạn sử dụng cả hai cùng một lúc, cho phép bạn sử dụng bất kỳ cú pháp nào bạn thích.


71

Thuộc tính đính kèm là một loại thuộc tính phụ thuộc. Sự khác biệt là ở cách chúng được sử dụng.

Với thuộc tính được đính kèm, thuộc tính được định nghĩa trên một lớp không cùng lớp mà nó đang được sử dụng. Điều này thường được sử dụng để bố trí. Ví dụ tốt là Panel.ZIndex hoặc Grid.Row - bạn áp dụng điều này cho một điều khiển (ví dụ: Nút), nhưng nó thực sự được định nghĩa trong Panel hoặc Grid. Thuộc tính được "đính kèm" với phiên bản của nút.

Điều này cho phép một vùng chứa, ví dụ, tạo ra các thuộc tính có thể được sử dụng trên bất kỳ UIelement nào.

Đối với sự khác biệt về triển khai - về cơ bản nó chỉ là vấn đề của việc sử dụng Register so với RegisterAttached khi bạn xác định thuộc tính.


10
Nhưng sự khác biệt chính xác là gì ?! Từ những gì tôi đã thấy, bạn có thể đính kèm một thuộc tính không thể đính kèm với một thuộc tính khác thông qua mã (tôi nghĩ rằng điều này bị chặn trong XAML.) Có lẽ đó là sự khác biệt?
Mark A. Donohoe

5

Các thuộc tính được đính kèm về cơ bản là dành cho các phần tử vùng chứa. Giống như nếu bạn có lưới và bạn có lưới. đặt trong lưới.

Thuộc tính phụ thuộc giống như thuộc tính về cơ bản thuộc về một số lớp khác và được sử dụng trong lớp khác. Ví dụ: giống như bạn có một hình chữ nhật ở đây chiều cao và chiều rộng là thuộc tính thông thường của hình chữ nhật, nhưng bên trái và trên cùng là thuộc tính phụ thuộc vì nó thuộc về lớp Canvass.


-1

Thuộc tính được đính kèm là một loại DependencyProperties đặc biệt. Chúng cho phép bạn đính kèm một giá trị vào một đối tượng không biết gì về giá trị này. Một ví dụ điển hình cho khái niệm này là bảng bố cục. Mỗi bảng điều khiển bố cục cần dữ liệu khác nhau để căn chỉnh các phần tử con của nó. Canvas cần Top và Left, DockPanel cần Dock, v.v. Vì bạn có thể viết bảng layout của riêng mình nên danh sách là vô hạn. Vì vậy, bạn thấy đó, không thể có tất cả các thuộc tính đó trên tất cả các điều khiển WPF. Các giải pháp là thuộc tính đính kèm. Chúng được xác định bởi điều khiển cần dữ liệu từ điều khiển khác trong ngữ cảnh cụ thể. Ví dụ: một phần tử được căn chỉnh bởi bảng điều khiển bố cục mẹ.


-1

Tôi nghĩ bạn có thể định nghĩa thuộc tính đính kèm trong chính lớp đó hoặc bạn có thể định nghĩa nó trong một lớp khác. Chúng tôi luôn có thể sử dụng thuộc tính đính kèm để mở rộng các điều khiển microsoft tiêu chuẩn. Nhưng thuộc tính phụ thuộc, bạn xác định nó trong điều khiển tùy chỉnh của riêng bạn. ví dụ: Bạn có thể kế thừa quyền kiểm soát của mình từ một điều khiển tiêu chuẩn và xác định một thuộc tính phụ thuộc trong quyền kiểm soát của riêng bạn và sử dụng nó. Điều này tương đương với việc xác định thuộc tính đính kèm và sử dụng thuộc tính đính kèm này trong điều khiển tiêu chuẩn.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.