INotifyPropertyThay đổi so với DependencyProperty trong ViewModel


353

Khi triển khai ViewModel trong ứng dụng WPF của Model-View-ViewModel, dường như có hai lựa chọn chính để làm cho nó có thể lưu trữ dữ liệu. Tôi đã thấy các triển khai sử dụng DependencyPropertycho các thuộc tính mà View sẽ liên kết và tôi đã thấy triển khai ViewModel INotifyPropertyChangedthay thế.

Câu hỏi của tôi là khi nào tôi nên thích cái này hơn cái kia? Có sự khác biệt hiệu suất? Có thực sự là một ý tưởng tốt để cung cấp các phụ thuộc ViewModel cho WPF không? Tôi cần xem xét điều gì khác khi đưa ra quyết định thiết kế?


11
xem stackoverflow.com/questions/1329138/ trên để biết trình biên dịch đã kiểm tra cách triển khai INotifyPropertyChanged. Tránh có tên tài sản là một chuỗi ma thuật.
Ian Ringrose

10
Nói chung, có một sự khác biệt lớn giữa một thuộc tính phụ thuộc và một thuộc tính bình thường trong một lớp thực hiện INotifyPropertyChanged. Các thuộc tính phụ thuộc có thể là nguồn hoặc đích trong liên kết dữ liệu nhưng các thuộc tính thông thường có hỗ trợ INotifyPropertyChanged chỉ có thể được sử dụng làm nguồn. Vì vậy, các giải pháp này không thể thay thế cho nhau. Cơ sở hạ tầng liên kết dữ liệu yêu cầu DP làm mục tiêu để hoạt động, nhưng nguồn có thể là một thuộc tính bình thường với sự hỗ trợ INotifyPropertyChanged hoặc DP chung.
Mostafa Rezaei

4
Xem stackoverflow.com/a/10595688/200442 để biết cách triển khai .net 4.5 INotifyPropertyChanged.
Daniel Little

giải thích tốt nhất ở đây stackoverflow.com/a/3552550/366064
Bizhan

Câu trả lời:


214

Kent đã viết một blog thú vị về chủ đề này: Xem Mô hình: POCOs so với DependencyObjects .

Tóm tắt ngắn gọn:

  1. DependencyObjects không được đánh dấu là tuần tự hóa
  2. Lớp DependencyObject ghi đè và niêm phong các phương thức Equals () và GetHashCode ()
  3. Một DependencyObject có ái lực của luồng - nó chỉ có thể được truy cập trên luồng mà nó được tạo

Tôi thích cách tiếp cận POCO. Một lớp cơ sở cho PresentationModel (còn gọi là ViewModel) thực hiện giao diện INotifyPropertyChanged có thể được tìm thấy ở đây: http://componymousextensions.codeplex.com


24
DependencyObject cũng phụ thuộc vào các thư viện WPF, trong khi POCO thì không, cho phép các mô hình xem của bạn điều khiển một số ngăn xếp UI khác nơi WPF không khả dụng (Compact Framework, Mono).
codekaizen

26
Khi đó, rõ ràng là Thuộc tính phụ thuộc chỉ được xây dựng cho giao diện người dùng chứ không dành cho lớp doanh nghiệp.
Andrei Rînea

11
Thuộc tính phụ thuộc cũng yêu cầu cha mẹ DependencyObject. ViewModel của bạn thực sự không nên kế thừa từ DependencyObject.
Gusdor

38

Theo hướng dẫn hiệu suất WPF, DependencyObjects chắc chắn hoạt động tốt hơn các POCO triển khai INotifyPropertyChanged:

http://msdn.microsoft.com/en-us/l Library / bb613546.aspx


1
Tôi phải đồng ý về điều đó ;-): blog.lexique-du-net.com/index.php?post/2010/02/24/ Kẻ
Jonatha ANTOINE

Nếu bạn chọn .NET Framework phiên bản 4, thì liên kết vẫn hoạt động. Nó chỉ không có sẵn cho "phiên bản hiện tại".
doubleYou 17/03/2016

Cảm ơn bạn đã chỉ ra điều này, có rất nhiều thông tin sai lệch tai tiếng về việc các nhà phát triển đưa ra tuyên bố mặn mà rằng INotifyPropertyChanged nhanh hơn hoặc phát sinh ít chi phí hơn DP và đơn giản là không có cơ sở. DP là những cách nhanh chóng, thanh lịch và mạnh mẽ để xác định cấu trúc cây ảo (dữ liệu).
sinh viên

Có một điều ác tiềm ẩn đối với DependencyObjects. Chúng cần được tạo trên cùng một luồng với các điều khiển liên kết với chúng. Điều đó có nghĩa là chủ đề GUI. Điều đó có nghĩa là bạn cần phải điều chỉnh việc tạo ra chủ đề đó. Bạn không thể tải những thứ đó và tạo trên một số luồng nền từ DB. Trừ khi bạn gửi đi sự sáng tạo. Điên.
ed22

28

Sự lựa chọn hoàn toàn dựa trên mức độ trừu tượng hóa logic và giao diện người dùng của bạn. Nếu bạn không muốn một sự tách biệt tốt thì DP sẽ làm việc cho bạn.

DependencyProperies sẽ được áp dụng chủ yếu ở cấp độ VisualElements, vì vậy sẽ không tốt nếu chúng tôi tạo ra nhiều DP cho mỗi yêu cầu kinh doanh của mình. Ngoài ra, có một chi phí lớn hơn cho DP so với INotifyPropertyChanged. Khi bạn thiết kế WPF / Silverlight, hãy cố gắng thiết kế UI và ViewModel hoàn toàn tách biệt để tại bất kỳ thời điểm nào, chúng tôi có thể thay đổi các điều khiển Bố cục và Giao diện người dùng (Dựa trên chủ đề và Kiểu)

Cũng tham khảo bài đăng này - /programming/275098/what-appluggest-could-i-study-to-understand-datamodel-view-viewmodel . Liên kết có rất nhiều tham chiếu đến mẫu Model-View-ViewModel, rất phù hợp với cuộc thảo luận này.


9
Bài đăng của jbe trả lời chính xác hơn. Chỉ vì VM (hoặc Người thuyết trình) kế thừa từ DependencyObject không có nghĩa là nó không thể được tạo kiểu hoặc không tách biệt về mặt logic với Chế độ xem, điều đó chỉ có nghĩa là việc lưu trữ cho các giá trị thuộc tính khác với các trường được khai báo rõ ràng trong Phong cách POCO. Điều đó đang được nói, tuần tự hóa, bình đẳng logic và mối quan hệ luồng là những vấn đề thực sự mà các máy ảo dựa trên DepedencyObject phải giải quyết.
micahtan

"Ngoài ra có một chi phí lớn hơn cho DP so với INotifyPropertyChanged" - nguồn chứng minh của bạn về vấn đề này ở đâu? Rất nhiều nhà phát triển đưa ra tuyên bố này mà không có bất kỳ bằng chứng nào hỗ trợ nó. Theo MSDN thì không đúng. "Cố gắng thiết kế UI và ViewModel hoàn toàn tách biệt để tại bất kỳ thời điểm nào chúng tôi có thể thay đổi các điều khiển Bố cục và Giao diện người dùng" - một lần nữa, điều này hoàn toàn không liên quan gì đến POCO + PropChange so với DO / DP. Nếu bất cứ điều gì, đăng ký Reflection và Path trong DO / DP cải thiện khả năng của bạn để làm việc về mặt trực quan.
sinh viên

20

Từ quan điểm biểu cảm, tôi hoàn toàn thích sử dụng các thuộc tính phụ thuộc và co rúm khi nghĩ đến INotifyPropertyChanged. Ngoài stringtên tài sản và rò rỉ bộ nhớ có thể do đăng ký sự kiện, INotifyPropertyChangedlà một cơ chế rõ ràng hơn nhiều.

Thuộc tính phụ thuộc ngụ ý "khi này, làm điều đó" bằng cách sử dụng siêu dữ liệu tĩnh dễ hiểu. Đó là một cách tiếp cận tuyên bố nhận được phiếu bầu của tôi cho sự thanh lịch.


1
Phần chuỗi bây giờ có một giải pháp với toán tử nameof.
Newtopian

@Newtopian: Đúng. Ngoài ra còn có một số điều thú vị có thể với [CallerMemberName].
Bryan Watts

Chưa kể đến sự giàu có của các lợi ích Đăng ký tài sản (Reflection) trong WPF và CLR khi sử dụng mô hình DO / DP so với POCO.
sinh viên

16

INotifyPropertyChanged khi được sử dụng cũng cung cấp cho bạn khả năng thêm logic hơn trong mã của getters và setter của các thuộc tính của bạn.

DependencyProperty thí dụ:

public static DependencyProperty NameProperty = DependencyProperty.Register( "Name", typeof( String), typeof( Customer ) );

public String Name
{
    set { SetValue( NameProperty, value ); }
    get { return ( String ) GetValue( NameProperty ); }
}

Trong getter và setter của bạn --- tất cả những gì bạn có thể làm chỉ đơn giản là gọi SetValue và GetValue tương ứng, b / c trong các phần khác của khung mà getter / setter không được gọi, thay vào đó nó gọi trực tiếp SetValue, GetValue, do đó logic thuộc tính của bạn sẽ không đáng tin cậy được thực hiện.

Với INotifyPropertyChanged, xác định một sự kiện:

public event PropertyChangedEventHandler PropertyChanged;

Và sau đó chỉ cần có bất kỳ logic nào ở bất kỳ đâu trong mã của bạn, sau đó gọi:

// ...
// Something cool...
// ...

if( this.PropertyChanged != null )
{
    PropertyChanged( this, new PropertyChangedEventArgs( "Name" ) );
}

// More cool stuff that will reliably happen...

Điều này có thể là trong một getter / setter, hoặc bất cứ nơi nào khác.


11
Bạn cũng có thể nhận thông báo thay đổi từ DependencyProperIES. Xem PropertyMetadata.PropertyChangedCallback. Ví dụ tại: msdn.microsoft.com/en-us/l Library / ms745795.aspx
Joe White

2
Ngoài ra, bạn cũng có thể gọi SetValue từ bất cứ đâu, không chỉ từ bên trong khách sạn
aL3891

Điều này là sai lệch và không đúng sự thật - có nhiều cách để nối vào các sự kiện thay đổi trên DP, ngay cả khi nó đã thay đổi 'nội bộ'. Một trong số chúng được Joe White
tpartee

16

Các thuộc tính phụ thuộc nhằm hỗ trợ liên kết (dưới dạng mục tiêu) trên các thành phần UI không phải là nguồn để liên kết dữ liệu, đây là lúc INotifyProperty xuất hiện. Từ quan điểm thuần túy bạn không nên sử dụng DP trên ViewModels.

"Để trở thành nguồn của ràng buộc, một thuộc tính không cần phải là thuộc tính phụ thuộc, bạn có thể sử dụng bất kỳ thuộc tính CLR nào làm nguồn ràng buộc. Tuy nhiên, để trở thành mục tiêu của ràng buộc, thuộc tính phải là một Thuộc tính phụ thuộc. Để liên kết một chiều hoặc hai chiều có hiệu lực, thuộc tính nguồn phải hỗ trợ các thông báo thay đổi lan truyền đến hệ thống liên kết và do đó là mục tiêu. Đối với các nguồn ràng buộc CLR tùy chỉnh, điều này có nghĩa là thuộc tính phải hỗ trợ INotifyPropertyChanged. Bộ sưu tập nên hỗ trợ INotifyCollectionChanged. "

Tất cả các đối tượng phụ thuộc không thể được tuần tự hóa (Điều này có thể cản trở việc sử dụng ViewModels và DTO (POCO).

Có sự khác biệt giữa DP trong Silverlight so với WPF.

http://msdn.microsoft.com/en-us/l Library / cc221408 (v = VS.95) .aspx

http://msdn.microsoft.com/en-us/l Library / cc903933 (VS.95) .aspx


Tôi đã sử dụng các Đối tượng phụ thuộc được tuần tự hóa từ năm 2009 mà không có vấn đề gì, vì vậy không chắc bạn đang nói về điều gì khi bạn nói "Tất cả các đối tượng phụ thuộc không thể được tuần tự hóa" - vâng, chúng có thể. Trong thực tế, có rất nhiều lựa chọn: codeproject.com/Articles/61440/, ty thress.net/2008/11/11/dependencyproperty-serialization Và một trong những mục yêu thích cá nhân của tôi: chỉ cung cấp các cửa hàng sao lưu cho tất cả các DP của bạn và thực hiện các tuần tự đó ( không có ví dụ đơn giản nào có sẵn trong 2 phút tìm kiếm trên Google, nhưng tôi đảm bảo với bạn điều này hoạt động).
sinh viên

7

Tôi cũng đã phải xem xét quyết định này gần đây.

Tôi thấy rằng cơ chế INotifyPropertyChanged phù hợp với nhu cầu của tôi hơn vì nó cho phép tôi dán GUI của mình vào khung logic nghiệp vụ hiện có mà không bị trùng lặp trạng thái. Khung tôi đang sử dụng có mẫu quan sát riêng và dễ dàng chuyển tiếp một cấp thông báo sang cấp tiếp theo. Tôi chỉ đơn giản là có một lớp thực hiện giao diện người quan sát từ khung logic nghiệp vụ của tôi và giao diện INotifyPropertyChanged.

Với DP, bạn không thể tự xác định phụ trợ lưu trữ trạng thái. Tôi sẽ phải để .net lưu trữ một bản sao của mọi mục trạng thái mà tôi đã ràng buộc. Điều này có vẻ như một chi phí không cần thiết - trạng thái của tôi rất lớn và phức tạp.

Vì vậy, ở đây tôi thấy INotifyPropertyChanged tốt hơn để hiển thị các thuộc tính từ logic nghiệp vụ sang GUI.

Điều đó được nói rằng khi tôi cần một tiện ích GUI tùy chỉnh để hiển thị một thuộc tính và để các thay đổi đối với thuộc tính đó ảnh hưởng đến các tiện ích GUI khác DP đã chứng minh giải pháp đơn giản.

Vì vậy, tôi thấy DP hữu ích cho thông báo GUI đến GUI.


6

Có thực sự là một ý tưởng tốt để cung cấp các phụ thuộc ViewModel cho WPF không?

.NET 4.0 sẽ có System.Xaml.dll, vì vậy bạn sẽ không phải phụ thuộc vào một khung tùy ý để sử dụng nó. Xem bài viết của Rob Relyea về phiên PDC của anh ấy.

Tôi lấy

XAML là ngôn ngữ để mô tả các đối tượng và WPF là một khung có các đối tượng được mô tả là các thành phần UI.

Mối quan hệ của họ tương tự như C #, một ngôn ngữ để mô tả logic và .NET, một khung công tác thực hiện các loại logic cụ thể.

Mục đích của XAML là đồ thị đối tượng khai báo. Các công nghệ W * F là ứng cử viên tuyệt vời cho mô hình này, nhưng XAML tồn tại độc lập với chúng.

XAML và toàn bộ hệ thống phụ thuộc đã được triển khai như các ngăn xếp riêng biệt cho WF và WPF, có lẽ để tận dụng trải nghiệm của các nhóm khác nhau mà không tạo ra sự phụ thuộc (không có ý định chơi chữ) giữa chúng.


Bằng cách trả lời, dường như bạn đang đưa ra giả định rằng bitbonk coi XAML và WPF là như nhau. ViewModels nên có càng ít phụ thuộc WPF càng tốt, không làm tăng sự phân tách logic, nhưng để giảm độ phức tạp của mã và tránh tất cả các vấn đề liên quan đến việc viết logic đơn giản trong mã phía sau của điều khiển người dùng. Bạn chắc chắn sẽ triển khai các khái niệm WPF như ICommand và hành vi hiện tại mà chỉ WPF / Silverlight mới có thể dễ dàng bao bọc - mối quan tâm luồng trình bày duy nhất của bạn trong mô hình chế độ xem phải là CollectionViews và ObservableCollection.
Gusdor

6

Thuộc tính phụ thuộc là chất keo của việc tạo điều khiển tùy chỉnh. Nếu bạn quan tâm đến việc sử dụng Intelli-sense để hiển thị các thuộc tính của bạn trong cửa sổ thuộc tính tại thời điểm thiết kế XAML, bạn phải sử dụng các thuộc tính Phụ thuộc. INPC sẽ không bao giờ hiển thị một thuộc tính trong cửa sổ thuộc tính tại thời điểm thiết kế.


4

Có vẻ như Thuộc tính phụ thuộc nên được sử dụng trong các điều khiển mà bạn tạo, chẳng hạn như Nút. Để sử dụng các thuộc tính trong XAML và sử dụng tất cả các tính năng WPF, các thuộc tính đó phải thuộc tính phụ thuộc.

Tuy nhiên, ViewModel của bạn tốt hơn khi sử dụng INotifyPropertyChanged. Sử dụng INotifyPropertyChanged sẽ cung cấp cho bạn khả năng có logic getter / setter nếu bạn cần.

Tôi khuyên bạn nên kiểm tra phiên bản lớp cơ sở của Josh Smith cho ViewModel đã triển khai INotifyPropertyChanged:

http://joshsmithonwpf.wordpress.com/2007/08/29/a-base- class-who-implements-inotifypropertychanged /

Tôi nghĩ rằng đây là một ví dụ tuyệt vời về cách thực hiện ViewModel.


4

Tôi nghĩ DependencyProperty và INotifyPropertyChanged được sử dụng cho hai thứ khác nhau trong Binding: thứ nhất để cho phép một thuộc tính trở thành mục tiêu của một ràng buộc và nhận đầu vào từ một thuộc tính khác (sử dụng {Binding ...} để đặt thuộc tính), cuối cùng khi bạn muốn giá trị của một thuộc tính được sử dụng làm nguồn của một ràng buộc (tên trong Biểu thức đường dẫn liên kết). Vì vậy, sự lựa chọn chỉ đơn thuần là kỹ thuật.


2
Một INotifyPropertyChanged có thể được sử dụng trong cả hai trường hợp. Bạn có thể ràng buộc TwoWay với nó. DependencyProperty được yêu cầu vì lý do kỹ thuật chỉ đối với một số hành động được thực hiện trên đối tượng View (ví dụ: đặt một số thuộc tính khi khởi tạo đối tượng View trong XAML). Không bao giờ cần phải có DependencyProperty cho ViewModel.
oillio

3

Tôi thích một cách tiếp cận trực tiếp hơn, mà tôi đã viết blog trong Mô hình trình bày mà không có INotifyPropertyChanged . Sử dụng thay thế cho liên kết dữ liệu, bạn có thể liên kết trực tiếp với các thuộc tính CLR mà không cần bất kỳ mã kế toán nào. Bạn chỉ cần viết mã .NET đơn giản trong Mô hình xem của mình và nó được cập nhật khi Mô hình dữ liệu của bạn thay đổi.


Không có INotifyPropertyChanged, PropertyDescriptorđược sử dụng, gây rò rỉ bộ nhớ
Tilak

Thư viện Kiểm soát cập nhật mà tôi trình bày trong bài đăng trên blog đó sử dụng các tài liệu tham khảo yếu, không phải mô tả thuộc tính. Nó không bị rò rỉ bộ nhớ.
Michael L Perry

1
Michael, thư viện của bạn tạo ra rất nhiều mã. Tôi không thấy lợi ích. Tôi có thể đạt được điều tương tự bằng cách tạo trình bao bọc mô hình với các cuộc gọi sự kiện PropertyChanged được tạo.
Der_Meister

3

Chỉ có một điều tại sao thích một DependencyObject- Binding sẽ hoạt động tốt hơn. Chỉ cần thử một ví dụ với danh sách ListBoxTextBox, điền dữ liệu từ thuộc INotifyPropertyChangedtính so với DependencyPropertyvà chỉnh sửa mục hiện tại từ TextBox...


1
Mẫu mã, xin vui lòng
Hassan Tareq

1

Nếu bạn muốn hiển thị các thuộc tính cho các điều khiển khác, bạn phải sử dụng các thuộc tính Phụ thuộc ... Nhưng chúc may mắn vì chúng mất một thời gian để tìm ra ...

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.