相依性屬性值的優先順序 (WPF .NET)
Windows Presentation Foundation (WPF) 屬性系統的運作方式會影響相依性屬性的值。 此文章說明 WPF 屬性系統中各種以屬性為基礎之輸入的優先順序如何決定相依性屬性的有效值。
必要條件
此文章假設您具備相依性屬性的基本知識,而且已閱讀相依性屬性概觀。 若要遵循此文章中的範例,建議您先熟悉 Extensible Application Markup Language (XAML),並了解如何撰寫 WPF 應用程式。
WPF 屬性系統
WPF 屬性系統會使用各種因素來決定相依性屬性的值,例如,即時屬性驗證、晚期繫結,以及相關屬性的屬性變更通知。 雖然用來決定相依性屬性值的順序和邏輯很複雜,但學習它可協助您避免不必要的屬性設定,同時也可找出為什麼嘗試設定相依性屬性不會產生預期值的原因。
相依性屬性設定於多個位置
下列 XAML 範例說明按鈕 Background (部分機器翻譯) 屬性上的三個不同「設定」作業如何影響其值。
<StackPanel>
<StackPanel.Resources>
<ControlTemplate x:Key="ButtonTemplate" TargetType="{x:Type Button}">
<Border Background="{TemplateBinding Background}" BorderThickness="{TemplateBinding BorderThickness}"
BorderBrush="{TemplateBinding BorderBrush}">
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center" />
</Border>
</ControlTemplate>
</StackPanel.Resources>
<Button Template="{StaticResource ButtonTemplate}" Background="Red">
<Button.Style>
<Style TargetType="{x:Type Button}">
<Setter Property="Background" Value="Blue"/>
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="Yellow" />
</Trigger>
</Style.Triggers>
</Style>
</Button.Style>
Which color do you expect?
</Button>
</StackPanel>
在範例中,Background
屬性會在本機設定為 Red
。 不過,在按鈕範圍中宣告的隱含樣式會嘗試將 Background
屬性設定為 Blue
。 而且,當滑鼠停留在按鈕上方時,隱含樣式中的觸發程序會嘗試將 Background
屬性設定為 Yellow
。 除了強制型轉和動畫之外,本機設定的屬性值具有最高的優先順序,因此按鈕將為紅色,即使在滑鼠停留時也一樣。 但是,如果您從按鈕中移除本機設定的值,則將從樣式中取得其 Background
值。 在樣式中,會優先執行觸發程序,因此,按鈕在滑鼠停留時將為黃色,否則為藍色。 此範例會取代按鈕的預設 ControlTemplate (部分機器翻譯),因為預設範本具有硬式編碼的滑鼠停留 Background
值。
相依性屬性優先順序清單
下列清單是屬性系統在為相依性屬性指派執行階段值時所使用的明確優先順序。 最高優先順序會先列出。
屬性系統強制型轉: 如需強制型轉的詳細資訊,請參閱強制型轉和動畫 (部分機器翻譯)。
作用中動畫或具有 Hold 行為的動畫: 動畫值必須優先於基底 (非動畫) 值,才能有實際效果,即使該值是在本機設定也一樣。 如需詳細資訊,請參閱強制型轉和動畫 (部分機器翻譯)。
本機值。 您可以透過方便的「包裝函式」屬性 (相當於在 XAML 中設定屬性 (Attribute 或 Property) 元素),或是使用特定執行個體的屬性 (Property) 呼叫 SetValue API,來設定本機值。 透過繫結或資源設定的本機值,其優先順序將與直接設定的值相同。
TemplatedParent 範本屬性值。 如果元素是透過範本 (ControlTemplate (部分機器翻譯) 或 DataTemplate (英文)) 所建立,則該元素具有 TemplatedParent (部分機器翻譯)。 如需詳細資訊,請參閱 TemplatedParent (部分機器翻譯)。 在
TemplatedParent
指定的範本內,優先順序為:觸發程序。
屬性 (Property) 集,通常是透過 XAML 屬性 (Attribute)。
隱含樣式。 僅適用於 Style 屬性。
Style
值是任意樣式資源,且 TargetType (部分機器翻譯) 值符合元素類型。 樣式資源必須存在於頁面或應用程式中。 查閱隱含樣式資源不會延伸至主題中的樣式資源。樣式觸發程序: 樣式觸發程序是明確或隱含樣式中的觸發程序。 樣式必須存在於頁面或應用程式中。 預設樣式中的觸發程序優先順序較低。
樣板觸發程序: 範本觸發程序是來自直接套用的範本或來自樣式內範本的觸發程序。 樣式必須存在於頁面或應用程式中。
樣式 setter 值。 樣式 setter 值是由樣式中的 Setter (部分機器翻譯) 所套用的值。 樣式必須存在於頁面或應用程式中。
預設樣式 (也稱為主題樣式)。 如需詳細資訊,請參閱預設 (主題) 樣式 (部分機器翻譯)。 在預設樣式中,優先順序為:
作用中的觸發程序。
Setter。
繼承: 子元素的某些相依性屬性會從父元素繼承其值。 因此,可能不需要在整個應用程式的每個元素上設定屬性值。 如需詳細資訊,請參閱屬性值繼承。
相依性屬性中繼資料的預設值。相依性屬性可以在該屬性的屬性系統註冊期間設定預設值。 繼承相依性屬性的衍生類別可根據類型覆寫相依性屬性中繼資料 (包括預設值)。 如需詳細資訊,請參閱相依性屬性中繼資料 (部分機器翻譯)。 針對繼承的屬性,父元素的預設值優先於子元素的預設值。 因此,如果未設定可繼承的屬性,則會使用根或父元素的預設值,而不是子元素的預設值。
TemplatedParent
TemplatedParent (部分機器翻譯) 優先順序不適用於直接在標準應用程式標記中宣告的元素屬性。 TemplatedParent
概念只對視覺化樹狀結構中因套用範本而產生的子項目有效。 當屬性系統搜尋由 TemplatedParent
指定的範本來尋找元素的屬性值時,其會搜尋建立該元素的範本。 來自 TemplatedParent
範本的屬性值通常就像在元素上本機設定值,但其優先順序會比實際的本機值低,因為範本有可能共用。 如需詳細資訊,請參閱TemplatedParent。
Style 屬性
優先順序的相同順序會套用至所有相依性屬性,但 Style (英文) 屬性除外。 Style
屬性是唯一的,因為其本身無法設定樣式。 不建議對 Style
屬性進行強制型轉或動畫處理 (對 Style
屬性進行動畫處理需要自訂動畫類別)。 因此,並非所有優先順序項目都適用。 只有三種方式可以設定 Style
屬性:
明確樣式: 元素的
Style
屬性會直接設定。Style
屬性值的作用就像是本機值,且與優先順序清單 (部分機器翻譯) 中的項目 3 具有相同的優先順序。 在大部分情況下,明確樣式不會內嵌定義,而是當成資源明確參考,例如Style="{StaticResource myResourceKey}"
。隱含樣式: 元素的
Style
屬性不會直接設定。 相反地,當樣式存在於頁面或應用程式中的某個層級時就會套用樣式,且具有符合樣式所套用之元素類型的資源索引鍵,例如<Style TargetType="x:Type Button">
。 類型必須完全相符,例如,即使MyButton
衍生自Button
,也不會將<Style TargetType="x:Type Button">
套用至MyButton
類型。Style
屬性值與優先順序清單 (部分機器翻譯) 中的項目 5 具有相同的優先順序。 您可以藉由呼叫 DependencyPropertyHelper.GetValueSource (部分機器翻譯) 方法、傳入Style
屬性並檢查結果中的ImplicitStyleReference
,來偵測隱含樣式值。預設樣式也稱為主題樣式。 元素的
Style
屬性不會直接設定。 相反地,其來自 WPF 展示引擎的執行階段主題評估。 在執行階段之前,Style
屬性值為null
。Style
屬性值與優先順序清單 (部分機器翻譯) 中的項目 9 具有相同的優先順序。
預設 (主題) 樣式
WPF 隨附的每個控制項都具有預設樣式 (可能因主題而有所不同),這就是為什麼預設樣式有時稱為「主題樣式」的原因。
ControlTemplate (部分機器翻譯) 是控制項預設樣式中的重要項目。 ControlTemplate
是樣式 Template (英文) 屬性的 setter 值。 如果預設樣式未包含範本,則沒有自訂範本作為自訂樣式一部分的控制項就沒有視覺外觀。 範本不僅會定義控制項的視覺外觀,也會定義在範本視覺化樹狀結構中的屬性與相對應控制項類別之間的連線。 每個控制項會公開一組屬性,這些屬性可以在不取代範本的情況下影響控制項的視覺外觀。 例如,考慮 Thumb (部分機器翻譯) 控制項的預設視覺外觀,其為 ScrollBar (部分機器翻譯) 元件。
Thumb (部分機器翻譯) 控制項具有特定的可自訂屬性。 Thumb
控制項的預設範本會建立一個基本結構或視覺化樹狀結構,其中包含數個巢狀 Border (部分機器翻譯) 元件,以建立斜面外觀。 在範本中,可由 Thumb
類別自訂的屬性會透過 TemplateBinding 來公開。 Thumb
控制項的預設範本具有各種框線屬性,這些屬性會與 Background (部分機器翻譯) 或 BorderThickness (部分機器翻譯) 等屬性共用範本繫結。 但是,如果屬性或視覺化排列方式的值是硬式編碼於範本中,或繫結至直接來自於主題的值,則您只能透過取代整個範本來變更那些值。 一般而言,如果屬性來自於範本化的父代且不是透過 TemplateBinding
來公開,則無法透過樣式變更屬性值,因為沒有便利的方式可將其設為目標。 但是,該屬性仍會受到已套用範本中的屬性值繼承所影響,或受到預設值所影響。
預設樣式會在其定義中指定 TargetType (部分機器翻譯)。 執行階段主題評估會使預設樣式的 TargetType
與控制項的 DefaultStyleKey (部分機器翻譯) 屬性相符。 相反地,隱含樣式的查閱行為會使用控制項的實際類型。 DefaultStyleKey
的值會由衍生類別繼承,因此,可能沒有相關聯樣式的衍生元素會取得預設的視覺外觀。 例如,如果您從 Button (部分機器翻譯) 衍生 MyButton
,MyButton
將會繼承 Button
的預設範本。 衍生類別可以覆寫相依性屬性中繼資料內 DefaultStyleKey
的預設值。 因此,如果您想要針對 MyButton
使用不同的視覺表示法,您可以覆寫 MyButton
上 DefaultStyleKey
的相依性屬性中繼資料,然後定義相關的預設樣式 (包括範本),而您將透過 MyButton
控制項進行封裝。 如需詳細資訊,請參閱控制項撰寫概觀。
動態資源
動態資源參考和繫結作業具有其設定位置的優先順序。 例如,套用至本機值的動態資源與優先順序清單 (部分機器翻譯) 中的項目 3 具有相同的優先順序。 另一個範例是,套用至預設樣式中屬性 setter 的動態資源繫結與優先順序清單 (部分機器翻譯) 中的項目 9 具有相同的優先順序。 由於動態資源參考和繫結必須從應用程式的執行階段狀態取得值,因此決定任何指定屬性之屬性值優先順序的流程會延伸至執行階段。
動態資源參考在技術上不是屬性系統的一部分,而且擁有其自己可與優先順序清單 (部分機器翻譯) 進行互動的查閱順序。 基本上,動態資源參考的優先順序是:元素到頁面根、應用程式、主題,然後是系統。 如需詳細資訊,請參閱 XAML 資源。
儘管動態資源參考和繫結作業具有其設定位置的優先順序,但值還是會延遲。 因此,如果您將動態資源或繫結設定為本機值,任何對本機值所做的變更都會完全取代動態資源或繫結。 即使您呼叫 ClearValue (英文) 方法清除本機設定的值,也無法還原動態資源或繫結。 事實上,如果您在具有動態資源或繫結 (沒有常值本機值) 的屬性上呼叫 ClearValue
,將會清除該動態資源或繫結。
SetCurrentValue
SetCurrentValue (部分機器翻譯) 方法是另一種設定屬性的方式,但不在優先順序清單 (部分機器翻譯) 中。 SetCurrentValue
可讓您變更屬性的值,而不需覆寫先前值的來源。 例如,如果屬性是由觸發程序所設定,然後您使用 SetCurrentValue
指派另一個值,則下一個觸發程序動作會將該屬性設定回觸發程序值。 每當您想要設定屬性值,但不想讓該值具有本機值的優先順序時,可以使用 SetCurrentValue
。 同樣地,您可以使用 SetCurrentValue
來變更屬性的值,而不需覆寫繫結。
強制型轉和動畫
強制型轉和動畫都會以「基底值」為根據。 「基底值」是具有最高優先順序的相依性屬性值,其決定方式是透過優先順序清單 (部分機器翻譯) 進行向上評估,直到達到項目 2 為止。
如果動畫未同時指定特定行為的 From (部分機器翻譯) 和 To (部分機器翻譯) 屬性值,或是動畫刻意在完成後還原成基底值,則基底值可能會對動畫值造成影響。 若要了解實際的情形,請執行目標值 (英文) 範例應用程式。 在範例中,針對矩形高度,請嘗試設定與任何 From
值不同的初始本機值。 範例動畫會使用 From
值 (而非基底值) 立即開始。 藉由將 Stop (部分機器翻譯) 指定為 FillBehavior (部分機器翻譯),動畫會在完成時,將屬性值重設為其基底值。 在動畫結束之後,就會使用正常優先順序來決定基底值。
您可以將多個動畫套用至單一屬性,每個動畫都有不同的優先順序。 WPF 展示引擎可能會根據動畫的定義方式和動畫值類型,將動畫值複合在一起,而不是套用優先順序最高的動畫。 如需詳細資訊,請參閱動畫概觀。
強制型轉位於優先順序清單 (部分機器翻譯) 頂端。 即使是執行中的動畫也受限於值強制型轉。 WPF 中的某些現有相依性屬性具有內建強制型轉。 針對自訂相依性屬性,您可以撰寫 CoerceValueCallback (英文) 以便在建立屬性時當作中繼資料一部分來傳遞,藉以定義強制型轉行為。 您也可以藉由在衍生類別中覆寫現有屬性上的中繼資料,來覆寫該屬性的強制型轉行為。 強制型轉與基底值互動的方式是,強制型轉上的條件約束會以其當時存在的形式套用,但仍保留基底值。 因此,如果強制型轉中的條件約束稍後放寬,強制型轉就會傳回可能最接近該基底值的值,而且強制型轉對屬性的影響可能會在所有條件約束都放寬之後停止。 如需強制型轉行為的詳細資訊,請參閱相依性屬性回呼和驗證 (部分機器翻譯)。
觸發程序行為
控制項通常會將觸發程序行為定義為其預設樣式 (部分機器翻譯) 的一部分。 在控制項上設定區域屬性可能會與那些觸發程序發生衝突,防止觸發程序 (以視覺方式或行為方式) 回應使用者驅動事件。 屬性觸發程序常用於控制項狀態屬性,例如 IsSelected (部分機器翻譯) 或 IsEnabled (部分機器翻譯)。 例如,根據預設,停用 Button (部分機器翻譯) 時,主題樣式觸發程序 (IsEnabled
是 false
) 會設定 Foreground (英文) 值,使 Button
顯示為灰色。如果您已設定本機 Foreground
值,則較高優先順序的區域屬性值將覆寫主題樣式 Foreground
值,即使停用 Button
也一樣。 設定覆寫控制項主題層級觸發程序行為的屬性值時,請小心不要過度干擾該控制項的預期使用者體驗。
ClearValue
ClearValue (英文) 方法會清除元素相依性屬性的任何本機套用值。 不過,呼叫 ClearValue
並不保證在屬性註冊期間於中繼資料建立的預設值是新的有效值。 優先順序清單(部分機器翻譯) 中的所有其他參與者仍在作用中,而且只會移除本機設定的值。 例如,如果您在具有主題樣式的屬性上呼叫 ClearValue
,則將套用主題樣式值作為新值,而不是以中繼資料為基礎的預設值。 如果您要將屬性值設定為已註冊的中繼資料預設值,則可透過查詢相依性屬性中繼資料來取得預設中繼資料,並呼叫 SetValue (部分機器翻譯) 在本機設定屬性值。