如何理解WPF中的依賴項屬性(DependencyProperty)?

我自己英語水平不是很好,之前看Charls老頭寫的Programming Windows 6th edition的時候,就被DependencyProperty這個東西給搞糊塗了,這本書我看了1/5就看不下去了(倒不是因為後面看不懂了,只是這個DependencyProperty讓我如梗在喉)(英語水平也確實不好,翻著有道詞典看書,比較吃力)

我以為是我英語水平的問題,於是去看中文版的WPF編程寶典。

然後發現越看越亂,越糊塗。。

後來又去淘寶買了個WPF的視頻教程,發現視頻教程里,完完全全的避開了這個知識點。。。。真是。。。嗶了狗了。。

希望有人能幫我一把,希望有人能形象的說明一下這個東西到底是個什麼玩意,為什麼微軟要搞出個DependencyProperty?

對所有答題者表示感激不盡。。

--------------------------------------------------------------------------------------------------

作為提問者,提出問題一星期之後,我已經初步搞明白了依賴屬性與依賴對象的思路了。

非常感謝諸位作答,只是諸位的答案於我來說不盡滿意,這並不是說諸位說的不對,只是我自身本身缺乏相關的知識儲備,很難通過諸位的答案就理解了DependencyProperty是什麼東西。

作為伸手黨,我的手太短了,諸位遞上來的大餅很好吃,只是我夠不著而已。。很慚愧啊。

其實我原本想自己寫一個詳細的答案回答一下自己的這個問題,供後來人參考,但想了想還是作罷了,我這麼渣,萬一誤導了誰,就不好了。

關於DependencyObject與DependencyProperty的相關知識,請參見《深入淺出WPF》這本書第七章,該書作者名為劉猛鐵。。書很好,非常好,相當好,十分好,太好了,與《深入理解C#》是一個量級上的書。

謝謝諸位。


我也是WPF新手,手機寫的有點亂,斗膽答一下,如果有錯一定要告訴俺!

所謂Dependency Property,是實現了一種機制,就是「允許以一般的方式,自動接收大部分的通知。」

換句話說啥意思呢,就是好比一棵視覺樹,頂層Element的某個屬性A被確定值以後,自動影響其子元素的屬性A的默認值。舉個例子,假設有一所女校,學校有個StudentGender屬性(附帶一個DependencyProperty),默認值是Female,那麼其學生也默認都是Female,當學校的屬性改變為Male的時候,其所有學生的默認性別都會變成Male。除非,你手動設定了某個學生為Female,那麼這個學生將不會變成Male。

所以說到這裡你應該能猜到了,DependencyProperty那妥妥必須是static的,而且,每種需要Gender變化的類型,都必須定義一個static GenderProperty

這是學生的

public static readonly DependencyProperty GenderProperty =

DependencyProperty.Register("Gender", typeof(GenderTypes),

typeof(Student), metadata,

ValidateCallBack);

這是學校的

FrameworkPropertyMetadata metadata = new FrameworkPropertyMetadata();

metadata.Inherits = true;

GenderProperty =

Student.GenderProperty.AddOwner(typeof(School));

GenderProperty.OverrideMetadata(typeof(School), metadata);

這麼一搞以後,學校更改性別的話,學生的性別就會跟著刷刷改了


廣告時間: MVVM-Sidekick 是個好框架。框架作者甚至可以直接現場幫你調代碼。

--------------------------

不怪題主 我們官方對外培訓的時候這個部分也基本是跳過的。我都是自己在培訓資料裡面加上這個章節給別人講,不然客戶也會不滿意。

==============================正文===============================

當你做綁定功能的時候 你需要一個事件告訴綁定的目標說,喂,我有個屬性變化了,你來取一下。

這個事件在一般的數據綁定類 INotifyProperyChanged 中 用string作為標識,哪個屬性變化了,string就會寫屬性的名字。比如說, class a的實例 A1的屬性P1變了,就會產生一個事件,

?我變了(A1,「P1」)

本質上把一個類當成一個字典,哪個項目變了就把string key傳遞出去。目標按照Key來獲取刷新值。

這是綁定行為基礎中的基礎。

-----------------------貌似沒有蛋用其實是原始需求的描述到此為止----------------------------

但是這樣的string會有很多問題,因為它太簡單,如果類 a 和類b 都有一個屬性叫做 P1 的話,會很難判斷是哪個類產生的。

如果想在 a 裡面放 b的一些信息,也會因為 string相等而無法插入。(比如布局屬性grid.row 就是在控制項強插grid類的key)

作為字典核心,string key也沒辦法表述value的類型,value的默認值 等metadata

於是任務變成了:設計一個 object key, 這個object 引用中有剛才提到的 名字,所屬類信息,值類型,默認值,更改後回調函數,等等信息。

這貨就是dp 。因為是每個類型static聲明出來的,所以同名的key也不相等

複雜綁定就可以藉此完成了

?我變了(按鈕1,Grid.RowProperty)

另外屬性還有其他需求

比如是否繼承父節點的同Key屬性,也要放在dp裡面

參考 @寂靜嶺 的答案。

-----------下面為更新思考-------------

A:簡答 IValueConverter 中兩個方法的參數從哪裡來

B:簡答為什麼WPF 中的對象可以在 Grid Canvas StackPanel 等多種定位容器中定位,而Winform只能支持上左 下右坐標定位。

C:簡答Behavior 是如何插入到任意一個 FrameworkElement中的。


其實數據綁定或者默認值繼承等等只是依賴屬性(DP)的一個側面。補充一點微軟做DP的動機。

首先WPF有很多非常有用的特性,比如數據綁定,基於Storyboard和Timeline的動畫系統,XAML Attached Property等等。具體的例子:TextBlock的Text屬性可以綁定在某個自定義類型的屬性上實現自動更新;可以用Blend或XAML在不寫代碼的情況下製作複雜的動畫;可以通過Grid.Row/Column或Canvas.X/Y來控制子控制項的布局。

那麼問題來了,這些強大的特性或者說設計目標如果使用標準的CLR屬性系統要如何實現呢?例如對控制項的Width或Height屬性應用動畫的時候Width和Height的值如何自動更新。

  • 讓開發者自己實現?
    • 麻煩,Bug多
    • 不可擴展
  • 框架內部追蹤和維護所有UI元素的屬性,比如動畫系統主動更新控制項Width?
    • 維護狀態的開銷太高
    • 在不升級Runtime的前提下很難高效實現

如果加上自動繼承Style和系統主題這些特性,或者以後Framework升級時添加更多功能的時候,問題就更加複雜了。所以使用CLR的屬性系統是不行的。

於是微軟為WPF設計了自己的屬性系統,而DP只是背後這個複雜系統的一個界面。DP的目的或者說本質是實現一種可以依賴外部輸入而計算其值的屬性。而CLR的屬性只能依據用戶代碼自身的狀態計算屬性值。

用戶在實現DP時暴露的介面依然是傳統的CLR屬性,但是背後不是用常見的backing field的方式實現,而是藉助DependencyObject的GetValue和SetValue。屬性值使用GetValue計算,這樣WPF可以做的事情就豐富太多啦,屬性值可以來源於:

  • 數據綁定
  • Storyboard動畫
  • 默認值
  • Override的默認值
  • 系統環境決定的值,如Theme等
  • Visual Tree中父元素的屬性
  • Attached Property
  • XAML中設定的值
  • 在特定條件下強制設定的邊界值
  • 來自未來新的子系統的值(擴展性)

這也是為什麼依賴屬性叫做依賴屬性,因為他可以依賴外部輸入而不只是自身狀態來計算值。思想上類似ECMAScript的prototype-based inheritance,ES的對象不只繼承結構和行為,還繼承狀態。而DP的基本思想也是擴展傳統CLR屬性的值的來源,使其表現出豐富的行為和高度的可擴展性。而在實現上DependencyObject也對DP值的source進行了緩存和優化,使其即使在複雜的動畫中也能保持很高的性能。

雖然第一次自己實現DP的時候多少會覺得有點反人類,但是考慮到DP帶給初學者和Designer的巨大便利,付出這點設計上的代價還是很值得的。

如何理解DP,微軟為何要搞DP?

DP是WPF強大的屬性系統的一個界面,豐富了屬性值的行為和來源。是高效實現數據綁定,Storyboard動畫,狀態繼承等等子系統和特性的基礎。


相當於是C#屬性的一種擴展,加了很多C#屬性沒辦法表示的東西


今天看了WPF相關DLL的反編譯源代碼(強烈推薦dotPeek反編譯工具),終於覺得自己有那麼一點點資格來完成這個答案了。下面的介紹僅從實現層面展開。

DependencyProperty(下文簡稱DP)既是一個普通類定義,又承擔著DP Manager的角色(static methods / fields)。作為一個類定義,我們來看看DP的幾個基本屬性:

private string _name;
private Type _propertyType; // DP值類型
private Type _ownerType; // 所屬DependencyObject類型

作為DP Manager,它是如何實現全局的DP管理的?就是下面這個簡單的數據結構:

private static Hashtable PropertyFromName = new Hashtable();

從這個表的key類(FromNameKey class)定義可以看出,DP是以(Name,OwnerType)屬性對來區分的。

從以上實現來看,DP的元數據集中存放於靜態哈希表中,以屬性名和所屬DependencyObject類型作為鍵值。

接下來我們想看的是一個DependencyObject(下稱DO),比如一個Button,是如何實現給它的DP賦值的。從public定義中能看到的賦值動作是通過

DependencyObject.SetValue()

方法完成的。深入探索這個方法,我們找到了存放該DO中所有DP對應值的數組:

private EffectiveValueEntry[] _effectiveValues;

看看EffectiveValueEntry大概有什麼屬性吧:

private object _value;
private short _propertyIndex;

其中_propertyIndex是某個DP的GlobalIndex,_value是對應的值,簡單明了。

綜上,如果忽略其他複雜的實現細節(Expression, Binding,Inherited等等),那麼DP類通過Hashtable存放所有DP的定義和元數據(以及預設值,這個在PropertyMetadata屬性中),而某個特定DP在一個DO對象中的值,則存放於該對象的EffectiveValueEntry數組中。有趣的是,我們發現_propertyIndex是short類型,也就是說,一個DO不能定義超過65535個DP。


What:

Dependency Properties Overview

The purpose of dependency properties is to provide a way to compute the value of a property based on the value of other inputs. These other inputs might include system properties such as themes and user preference, just-in-time property determination mechanisms such as data binding and animations/storyboards, multiple-use templates such as resources and styles, or values known through parent-child relationships with other elements in the element tree. In addition, a dependency property can be implemented to provide self-contained validation, default values, callbacks that monitor changes to other properties, and a system that can coerce property values based on potentially runtime information. Derived classes can also change some specific characteristics of an existing property by overriding dependency property metadata, rather than overriding the actual implementation of existing properties or creating new properties.

Why:

WPF Tutorial

When you begin to develop appliations with WPF, you will soon stumble across DependencyProperties. They look quite similar to normal .NET properties, but the concept behind is much more complex and powerful.

The main difference is, that the value of a normal .NET property is read directly from a private member in your class, whereas the value of a DependencyProperty is resolved dynamically when calling the GetValue() method that is inherited from DependencyObject.

When you set a value of a dependency property it is not stored in a field of your object, but in a dictionary of keys and values provided by the base class DependencyObject. The key of an entry is the name of the property and the value is the value you want to set.

The advantages of dependency properties are

  • Reduced memory footprint

    It"s a huge dissipation to store a field for each property when you think that over 90% of the properties of a UI control typically stay at its initial values. Dependency properties solve these problems by only store modified properties in the instance. The default values are stored once within the dependency property.

  • Value inheritance

    When you access a dependency property the value is resolved by using a value resolution strategy. If no local value is set, the dependency property navigates up the logical tree until it finds a value. When you set the FontSize on the root element it applies to all textblocks below except you override the value.

  • Change notification

    Dependency properties have a built-in change notification mechanism. By registering a callback in the property metadata you get notified, when the value of the property has been changed. This is also used by the databinding.

簡單的說,DP是WPF特別發明的設計,為了減少內存損耗,實現樣式繼承和響應值的變化。你完全可以不用這些然後自己設計一套。當初設計DP就是為了避免每個程序都自己實現一套這種東西並且提高WPF的性能。


我也是新手,我把我的理解說一下。題主可以參考一下。當然也可能有錯誤的地方,歡迎指正。

首先,凡是在xaml裡面能夠設置的屬性都是依賴屬性。例如 & 裡面的text就是textbox的一個依賴屬性。

MSDN中給出了下面幾種應用依賴屬性的場景:

1. 希望可在樣式中設置屬性。

2. 希望屬性支持數據綁定。

3. 希望可使用動態資源引用設置屬性。

4. 希望從元素樹中的父元素自動繼承屬性值。

5. 希望屬性可進行動畫處理。

6. 希望屬性系統在屬性系統、環境或用戶執行的操作或者讀取並使用樣式更改了屬性以前的值時報告。

7. 希望使用已建立的、WPF 進程也使用的元數據約定,例如報告更改屬性值時是否要求布局系統重新編寫元素的可視化對象。

像我這樣的新手用的最多的就是前4個,5、6和7以後肯定會有用。

說白了,這幾個功能,不用依賴屬性也能實現,但是你要寫太多的複雜代碼。用了依賴屬性可能就是簡單的幾句話。

最後說一下我自己的理解。感覺依賴屬性就像是一個屬性的鏈接,它自身的值大部分時候都是依賴於其他成員屬性的,也就是它的值一般都是動態的從其他值裡面取的。

如果你僅僅做後台,我猜你應該用不上依賴屬性。大多數都是自己做控制項,或者對已有控制項進行擴展,使得控制項在xaml中能夠進行額外的綁定時候才用到。


我認為之所以要有DP,主要是以下幾點:

1,為了省空間。一個Control可能有上百個屬性,如果使用普通屬性,對內存是個很大的負擔。但使用了DP,保持默認值的屬性就只需要在讀取時去詢問屬性定義就可以了,不必另外儲存它的實際Value。

2,為了可以使用Binding

3,利用defaultvalue和coercevaluecallback為DP提供了最基本的數據保證和最後的數據把關

如果你在學WPF時覺得不好理解,你可以去看看Silverlight的書籍,基本是WPF的簡化版,也比WPF的書薄很多,這樣比較容易理解XMAL的很多概念。


手機碼字渣排版。

依賴項屬性顧名思義,就是這不僅僅是一個屬性,還是一個可以被依賴的屬性。例如有一個string類型的依賴項屬性TextString,你可以告訴一個TextBox,讓他的Text依賴這個TextString,這樣當TextString發生改變時TextBox的Text也會隨著發生改變,也就是Text是什麼依賴於TextString。指定依賴誰的過程就是數據綁定。當然這個依賴可以是雙向的,通過在TextBox中輸入改變它的Text也會影響TextString。

依賴項屬性是不是有點多此一舉呢,有什麼價值嗎,畢竟TextBox也有TextChanged之類的事件啊,同樣TextString也可以在set中改變TextBox啊,可是在這個實現過程中都牽扯到了具體TextBox控制項的引用,邏輯和界面之間產生了關聯,如果哪天覺得TextBox不好,要換成TextBlock,你需要修改所有對TextBox的引用。如果是比較大的項目有專門的界面設計人員,那就很複雜了,程序員在開發前需要和界面開發人員確定好要使用的控制項類型,需求發生變化時不管改界面還是改業務邏輯都很麻煩。如果使用依賴項屬性,只需要把TextBox換成TextBlock,然後把Text綁定TextString就可以了。業務邏輯和界面是低耦合的,開發人員把注意力集中在業務邏輯和數據的處理上,通過操作數據影響到界面的變化,這也就是所謂的數據驅動。舉個栗子吧,答主剛開始用WPF是趕上扁平化流行,正好有個內部的小需求就做成扁平化風格了,結果被眾人圍起來批判太難看了。那麼就換界面吧,同一套業務邏輯不需要改動,界面布局好,該綁數據的綁數據,該綁命令的綁命令,一會就搞定了。到現在都是兩個窗體,用的同一個ViewModel。而依賴項屬性就是數據綁定中的關鍵。

題主要學習WPF一定要學慣用WPF的方式來開發,學習一下MVVM設計模式。說到這裡不得不感嘆下現在桌面開發確實不太受重視,WPF設計非常先進,個人覺得算是桌面開發的巔峰之作了,市面上書卻很少,題主看的WPF編程寶典用來入門確實難度偏大,這算是一本大而全的書,可以重點看下前幾章。我現在桌子上放著一本當字典用,有需求了隨時查閱。另外推薦下劉鐵猛的《深入淺出WPF》和他的博客以及視頻教程。MVVM沒有找到中文書,我看的好像叫Pro MVVM,劉鐵猛的視頻教程也有一部專門講MVVM的。入門了以後遇到問題上谷歌搜下英文基本都能解決。

雖然現在桌面開發不太受重視不過很多領域還是以桌面應用為主的,例如答主所在的領域目前還是以Winform開發為主,不過我們小範圍內已經完全轉成WPF了,可擴展性和可維護性真心強大。WPF學習曲線略感陡峭,希望答主堅持下去。Windows下桌面開發WPF絕對是最佳方式,另外期待Win10一桶漿糊。


依賴項熟悉才支持綁定更新


看了樓上的答案,應該是讓屬性可以被依賴也就是可以被其他屬性參照,在這個屬性變動時,其他屬性會自動更新,在gui編程中比較常見~

然後我說說叫動態屬性綁定吧~

直接通過Qt中的qml來理解的。

是對象就會有屬性和方法,屬性改變時就會告知系統,是誰的什麼屬性有變化,其他參照這個對象變化的屬性的 屬性 要及時進行跟進。

例如一個對象有年齡的屬性,隨著年齡的增長,你的身高就會變高,那麼你的身高屬性就可以通過年齡屬性來進行動態綁定。

也就是你的屬性發生變動時,會發出信號,換另一個角度講,一些屬性會自動更新自身

看看下面偽碼:

人 {
名字: "題主";
身高: 年齡*10;
年齡: 10;
}

每年長高10公分哦~

上面的代碼,每當題主長大一歲時,就會發出一個信號,我長高了(對象是題主,屬性是身高),然後在事務循環中,系統發現了這個信號,接下來就是要去找這個信號觸發某個操作(長高)。

然後題主可以看看Qt這個c++框架的qml,例如諸如動態屬性綁定,將c++對象導入到腳本運行環境(靜態語言的動態腳本化)。


其實winform裡面寫響應式的數據綁定模型也會手動實現類似的東西,WPF把它正規化了而已,多了語法層面的支持也更簡潔靈活。

造一遍輪子確實對理解框架設計意圖大有幫助。


這個開始是很難明白,開始的時候要先用起來,用的過程中再慢慢理解吧,籠統的說依賴項屬性就是為了對一些功能進行支持,那些功能如果都沒用過自然理解不了依賴項屬性


為數據綁定提供支持。看看字最多的那個答案,再結合代碼理解一下數據綁定的機制就懂了。


推薦這本書:《wpf深入淺出》


推薦閱讀:

學習C#的正確姿勢以及一些問題?
UWP 程序可以載入插件嗎?
請問c# 做wpf程序是如何將圖片嵌入到exe程序裡面的?
MFC 作為 Windows 原生的 GUI 庫還可以在項目中用嗎?
程序員如何挽救日漸失控的項目?

TAG:C# | Windows開發 | WindowsPresentationFoundationWPF | Dotnet |