WPF依賴(lài)屬性學(xué)習(xí)
當(dāng)前位置:點(diǎn)晴教程→知識(shí)管理交流
→『 技術(shù)文檔交流 』
概述WPF 依賴(lài)屬性(Dependency Property)是 WPF 框架的核心基礎(chǔ)設(shè)施之一,它擴(kuò)展了傳統(tǒng) .NET 屬性的能力,為 WPF 提供數(shù)據(jù)綁定、動(dòng)畫(huà)、樣式、繼承值、屬性值變更通知等高級(jí)功能。 為什么需要設(shè)計(jì)依賴(lài)屬性? 因?yàn)橐蕾?lài)屬性做到了CLR屬性沒(méi)做到的一些事情。 列舉幾個(gè)場(chǎng)景: 1、數(shù)據(jù)驅(qū)動(dòng) UI 的動(dòng)態(tài)性需要“可計(jì)算的值 在 WPF 里,綁定的值、樣式 Setter 的值、觸發(fā)器的值、動(dòng)畫(huà)幀的值,都是事后才知道的,甚至可以在運(yùn)行時(shí)不斷切換來(lái)源。 CLR 屬性:值寫(xiě)死在一個(gè)私有字段里,誰(shuí)最后 set 就留誰(shuí)。 依賴(lài)屬性:屬性系統(tǒng)先查看“當(dāng)前這一幀到底是誰(shuí)最有發(fā)言權(quán)”,再給出最終值——也就是“值是從外部來(lái)的,我只是按優(yōu)先級(jí)算一算”的依賴(lài)計(jì)算。 2、大規(guī)模對(duì)象樹(shù)的內(nèi)存壓力要求“默認(rèn)值共享 WPF 的控件樹(shù)隨隨便便成千上萬(wàn)實(shí)例,如果每個(gè) Button 都把 FontSize = 11 存一份 double,內(nèi)存就爆炸了。 依賴(lài)屬性把“默認(rèn)值”壓縮到一個(gè)靜態(tài)全局哈希表里,沒(méi)顯式設(shè)置的實(shí)例,直接查表用同一份值。 3、樣式 / 動(dòng)畫(huà) / 綁定 / 繼承 / 觸發(fā)器 / 資源多路輸入需要統(tǒng)一的“優(yōu)先級(jí)規(guī)則 同一個(gè) Background,可以是:本地值(紅),主題樣式(藍(lán)),動(dòng)畫(huà)(綠),觸發(fā)器(黃)…… 傳統(tǒng)屬性里誰(shuí)最后 set 誰(shuí)贏,根本無(wú)法表達(dá)這種“多源頭分時(shí)復(fù)用”的復(fù)雜策略。 4、跨父子樹(shù)的“屬性值繼承 典型例子:FontSize 設(shè)到 Window 上,所有子孫 TextBlock 直接復(fù)用該值,但中途隨時(shí)可以用樣式或本地值覆蓋。 傳統(tǒng)字段存儲(chǔ)實(shí)現(xiàn):父級(jí)改一次就要遞歸遍歷整棵樹(shù); 依賴(lài)屬性:子元素在取值時(shí)惰性向上詢(xún)問(wèn),邏輯/性能都優(yōu)雅。 學(xué)習(xí)依賴(lài)屬性在創(chuàng)建自定義的時(shí)候,創(chuàng)建一個(gè)依賴(lài)屬性的示例如下所示:
首先來(lái)看看命名,一個(gè)CLR屬性是Value,依賴(lài)屬性是ValueProperty,這是一種命名約定,可以很容易將這兩個(gè)東西關(guān)聯(lián)起來(lái)。 依賴(lài)屬性都是通過(guò)
OnValueChanged是變更回調(diào):
作用:值真正改變后通知控件更新 UI。 e 中包含舊值 e.OldValue 與新值 e.NewValue,可進(jìn)一步比較差異。 生命周期小結(jié)(一個(gè)賦值的全過(guò)程) 代碼 / 綁定 / 動(dòng)畫(huà)嘗試改變 Value。 WPF 調(diào)用 CoerceValue 讓控件有機(jī)會(huì)矯正值。 如果矯正后的值與當(dāng)前存儲(chǔ)值相同,流程結(jié)束;否則進(jìn)入下一步。 觸發(fā) OnValueChanged → 更新UI。 因?yàn)槭?BindsTwoWayByDefault,若存在綁定的源(ViewModel),其對(duì)應(yīng)屬性也會(huì)被同步。 現(xiàn)在大概了解了依賴(lài)屬性的設(shè)計(jì),你可能也聽(tīng)說(shuō)過(guò)“附加屬性”與“繼承屬性”。 其實(shí)官方并沒(méi)有“繼承屬性”這個(gè)稱(chēng)謂,繼承屬性只是將依賴(lài)屬性設(shè)置成可繼承罷了。 要想更好地理解依賴(lài)屬性的概念,一個(gè)很好的方式就是去看WPF的源碼,看看在源碼中是如何使用的,現(xiàn)在就讓我們一起去源碼中找找看吧?。?/p> 先來(lái)看看普通的依賴(lài)屬性定義: 目前我們接觸到了
這里官方源碼將按鈕是否按下這個(gè)屬性設(shè)置為了只讀依賴(lài)屬性,為什么官方是這樣做的呢? 想象一下一個(gè)按鈕的 如果你把它做成一個(gè)普通的可以隨意讀寫(xiě)的屬性:
那么任何代碼都可以修改它,這會(huì)破壞按鈕的內(nèi)在邏輯和行為一致性。 如果你把它做成一個(gè)普通的只讀屬性:
雖然外部代碼不能修改了,但這樣做有幾個(gè)缺點(diǎn): 不支持 WPF 高級(jí)功能:它不再是一個(gè)依賴(lài)屬性,因此無(wú)法享受數(shù)據(jù)綁定、樣式、動(dòng)畫(huà)、屬性值繼承等 WPF 的核心特性。比如,你無(wú)法在 XAML 中寫(xiě)一個(gè) Trigger 來(lái)在 IsPressed 為 true 時(shí)改變按鈕的背景色。 缺少變更通知:如果 _isPressed 的值改變了,WPF 的其他部分(比如 UI 渲染系統(tǒng))不會(huì)自動(dòng)知道。你需要手動(dòng)實(shí)現(xiàn) INotifyPropertyChanged 接口,這額外增加了復(fù)雜性。 為了解決上述問(wèn)題,WPF 引入了“只讀依賴(lài)屬性” (Read-Only Dependency Property) 。這種屬性擁有兩全其美的優(yōu)勢(shì): 對(duì)外是只讀的:保護(hù)了屬性的完整性,防止外部代碼隨意篡改。 內(nèi)部是可讀寫(xiě)的:屬性的“所有者”可以在特定邏輯下修改其值。 擁有依賴(lài)屬性的全部特性:支持?jǐn)?shù)據(jù)綁定、樣式、動(dòng)畫(huà)、觸發(fā)器等。 再來(lái)看看附加依賴(lài)屬性: Grid.Row是一個(gè)很經(jīng)典的附加依賴(lài)屬性。 注冊(cè)附加依賴(lài)屬性使用的是 附加屬性必須提供靜態(tài)的 在WPF中一個(gè)很經(jīng)典的可繼承依賴(lài)屬性的例子就是 使用了 這個(gè)枚舉類(lèi)有以下幾個(gè)選項(xiàng):
現(xiàn)在只是差不多了解了WPF中的依賴(lài)屬性的一些概念與使用,要想真正明白依賴(lài)屬性的設(shè)計(jì)與實(shí)現(xiàn),還得多研究研究源碼。 轉(zhuǎn)自https://www.cnblogs.com/mingupupu/p/19074134 該文章在 2025/9/5 8:49:29 編輯過(guò) |
關(guān)鍵字查詢(xún)
相關(guān)文章
正在查詢(xún)... |