相依性屬性值優先順序

本主題說明 Windows Presentation Foundation (WPF) 屬性系統的運作方式如何影響相依性屬性的值,並描述屬性系統各層面套用至屬性有效值的優先順序。

必要條件

本主題假設您從 WPF 類別上現有相依性屬性取用者的觀點瞭解相依性屬性,並已閱讀 相依性屬性概觀 。 若要遵循本主題中的範例,您也應該瞭解 XAML 並知道如何撰寫 WPF 應用程式。

WPF 屬性系統

WPF 屬性系統提供強大的方式,讓相依性屬性的值取決於各種因素,啟用即時屬性驗證、晚期系結,以及通知相關屬性變更其他屬性的功能。 用來決定相依性屬性值的實際順序和邏輯相當複雜。 不過,知道此順序將有助於避免不必要的屬性設定,也可釐清試圖影響或預測相依性屬性值,為何最後並未產生所預期的值。

相依性屬性可能在多處「設定」

以下是 XAML 範例,其中相同屬性 ( Background ) 有三個不同的「set」作業,可能會影響該值。

<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 屬性其值的最高優先順序。 如果從該 Button 執行個體移除區域數值 Red,則樣式會具有優先順序,因此按鈕會從樣式取得 Background 值。 在樣式內,觸發程序具有較高的優先順序,因此如果將滑鼠移至按鈕上方,按鈕會變成藍色,否則就會是綠色的。

相依性屬性設定優先順序清單

以下是屬性系統在指派相依性屬性的執行階段值時,所使用的決定順序。 最高優先順序會先列出。 此清單更進一步說明了相依性屬性概觀中所述的部分概要。

  1. 屬性系統強制型轉: 如需強制型轉的詳細資訊,請參閱本主題稍後的強制型轉、動畫和基底值

  2. 作用中動畫或具有 Hold 行為的動畫: 屬性的動畫必須優先於基底 (非動畫) 值,才能有任何實際效果,即使該值是在本機設定也一樣。 如需詳細資訊,請參閱本主題稍後的強制型轉、動畫和基底值

  3. 區域數值: 本機值可能透過「包裝函式」屬性的便利性來設定,這同樣等同于在 XAML 中設定為屬性或屬性元素,或是藉由使用特定實例的 屬性呼叫 SetValue API。 如果使用繫結或資源來設定區域數值,其在優先順序中的作用就如同設定直接值。

  4. TemplatedParent 範本屬性: 如果專案已建立為範本的一部分,則專案具有 TemplatedParentControlTemplateDataTemplate 。 如需何時套用此屬性的詳細資訊,請參閱本主題稍後的 TemplatedParent。 在範本內,優先順序如下:

    1. 來自範本的 TemplatedParent 觸發程式。

    2. 範本中的 TemplatedParent 屬性集(通常是透過 XAML 屬性)。

  5. 隱含樣式: 僅適用於 Style 屬性。 Style 屬性是由具有符合項目類型之索引鍵的任何樣式資源所填入。 該樣式資源必須位於頁面或應用程式中;查閱隱含樣式資源不會進行到主題中。

  6. 樣式觸發程序: 來自於頁面或應用程式之樣式內的觸發程序 (這些樣式可為明確或隱含樣式,但不能來自於預設樣式,預設樣式的優先順序較低)。

  7. 樣板觸發程序: 樣式內之範本或直接套用之範本中的任何觸發程序。

  8. 樣式 setter: 來自 Setter 頁面或應用程式樣式內的 值。

  9. 預設 (主題) 樣式: 如需何時套用此樣式,以及主題樣式與主題樣式內範本之關聯的詳細資訊,請參閱本主題稍後的預設 (主題) 樣式。 在預設樣式內,優先順序如下:

    1. 主題樣式中的作用中觸發程序。

    2. 主題樣式中的 setter。

  10. 繼承: 有幾個相依性屬性會從父項目繼承值到子項目,因此無須在整個應用程式的每一個項目上特別設定。 如需詳細資訊,請參閱屬性值繼承

  11. 來自相依性屬性中繼資料的預設值: 任何指定的相依性屬性都可能會有屬性系統註冊為該特定屬性建立的預設值。 此外,繼承相依性屬性的衍生類別可選擇根據類型覆寫該中繼資料 (包括預設值)。 如需詳細資訊,請參閱相依性屬性中繼資料。 因為會在預設值之前檢查繼承,所以對繼承的屬性而言,父項目的預設值會優先於子項目。 因此,如果未在任何位置設定可繼承的屬性,就會使用在根項目或父項目上指定的預設值,而不是子項目的預設值。

TemplatedParent

作為優先順序項目的 TemplatedParent 不會套用到您直接在標準應用程式標記中宣告之項目的任何屬性。 TemplatedParent 概念只對視覺化樹狀結構內因套用範本而產生的子項目有效。 當屬性系統搜尋 TemplatedParent 範本是否有值時,它會搜尋建立該專案的範本。 來自範本的 TemplatedParent 屬性值通常如同在子項目上設定為本機值,但此優先順序較低,與本機值存在,因為範本可能會共用。 如需詳細資訊,請參閱TemplatedParent

Style 屬性

稍早所述的查閱順序適用于所有可能的相依性屬性,但其中一個除外: Style 屬性。 屬性 Style 是唯一的,因為它本身無法設定樣式,因此不會套用優先順序專案 5 到 8。 此外,不建議使用動畫或強制 Style 功能(而且動畫 Style 需要自訂動畫類別)。 這會留下三種可能設定屬性的方式 Style

  • 明確樣式: 屬性 Style 會直接設定。 在大多數情況下,樣式不會內嵌定義,而是使用明確索引鍵將它當做資源參考。 在此情況下,Style 屬性本身就會當做區域數值,也就是優先順序項目 3。

  • 隱含樣式: 屬性 Style 未直接設定。 不過,存在於 Style 資源查閱順序中某個層級(頁面、應用程式),並使用符合要套用樣式類型的資源索引鍵進行索引鍵。 在此情況下, Style 屬性本身會依序列中識別為專案 5 的優先順序執行。 您可以針對 DependencyPropertyHelper 屬性來偵測此條件, Style 並在結果中尋找 ImplicitStyleReference

  • 預設樣式 ,也稱為 主題樣式。 屬性 Style 未直接設定,事實上會讀為 null 執行時間為止。 在此情況下,樣式來自屬於 WPF 簡報引擎一部分的執行時間主題評估。

對於不在主題中的隱含樣式,類型必須完全相符 - MyButtonButton 衍生類別不會隱含地使用 的 Button 樣式。

預設 (主題) 樣式

隨附于 WPF 的每個控制項都有預設樣式。 該預設樣式可能會視主題而不同,因此預設樣式有時也稱為主題樣式。

在控制項的預設樣式中找到的最重要資訊是其控制項範本,其存在於主題樣式中,做為其 屬性的 Template setter。 如果預設樣式沒有範本,則沒有自訂範本作為自訂樣式一部分的控制項將完全沒有視覺外觀。 預設樣式中的範本會為每個控制項的視覺外觀提供基本結構,而且也會定義在範本視覺化樹狀結構中定義的屬性與相對應控制項類別之間的連接。 每個控制項會公開一組屬性,這些屬性可影響控制項的視覺外觀,但不會完全取代範本。 例如,請考慮控制項的預設視覺外觀 Thumb ,這是 的 ScrollBar 元件。

Thumb具有特定的可自訂屬性。 的預設範本 Thumb 會建立具有數個巢狀 Border 元件的基本結構/視覺化樹狀結構,以建立斜面外觀。 如果屬於範本一部分的屬性是要公開類別的自訂 Thumb ,則該屬性必須由範本內的 TemplateBinding 公開 。 在 的案例 Thumb 中,這些框線的各種屬性會共用範本系結至 或 BorderThicknessBackground 屬性。 但特定其他屬性或視覺化排列方式是用硬式編碼加入控制項範本,或繫結至直接來自於主題的值,除了取代整個範本之外,無法以其他方式變更。 一般而言,如果屬性來自於樣板化父項目且不由範本繫結公開,就無法使用樣式調整,因為沒有簡單的方式可將它設為目標。 但該屬性仍會受到所套用範本中的屬性值繼承所影響,或受到預設值所影響。

主題樣式在其定義中使用類型作為索引鍵。 不過,當主題套用至指定的專案實例時,會藉由檢查 DefaultStyleKey 控制項上的 屬性來執行此類型的主題查閱。 這與隱含樣式使用常值 Type 的方式相反。 的值 DefaultStyleKey 會繼承至衍生類別,即使實作者未變更它(變更屬性的預定方式不是在屬性層級覆寫它,而是改為在屬性中繼資料中變更其預設值)。 這種間接方式可讓基底類別為沒有樣式 (或者更重要的是,在該樣式內沒有範本,因此完全沒有預設視覺外觀) 的衍生項目定義主題樣式。 因此,您可以衍生 MyButtonButton ,而且仍然會取得 Button 預設範本。 如果您是 的控制項作者 MyButton ,而且想要不同的行為,您可以覆寫 的 MyButton 相依性屬性中繼資料 DefaultStyleKey ,以傳回不同的索引鍵,然後定義相關的主題樣式,包括您必須搭配控制項封裝的 MyButton 範本 MyButton 。 如需主題、樣式和控制項撰寫的詳細資訊,請參閱控制項撰寫概觀

動態資源參考和繫結

動態資源參考和繫結作業會採用其設定位置的優先順序。 例如,套用到區域數值的動態資源會依優先順序項目 3 作用,主題樣式內屬性 setter 的繫結會依優先順序項目 9 套用,依此類推。 由於動態資源參考和繫結必須能夠從應用程式的執行階段狀態取得值,因此決定任何指定屬性之屬性值優先順序的實際程序也會延伸至執行階段。

嚴格來說,動態資源參考不是屬性系統的一部分,但它們有自己的查閱順序,會與上面所列的順序互動。 該優先順序在 XAML 資源中有更詳細的說明。 基本上來說,該優先順序就是:項目優先於頁面根項目、應用程式、主題和系統。

動態資源和繫結具有其設定位置的優先順序,但會受到其值影響。 因此,如果您將動態資源或繫結設定為區域數值,區域數值的任何變更都會取代整個動態資源或繫結。 即使您呼叫 ClearValue 方法來清除本機設定的值,也不會還原動態資源或系結。 事實上,如果您在具有動態資源或系結的屬性上呼叫 ClearValue (沒有常值本機值),呼叫也會清除 ClearValue 它們。

SetCurrentValue

方法 SetCurrentValue 是設定屬性的另一種方式,但不是優先順序。 相反地, SetCurrentValue 可讓您變更屬性的值,而不覆寫先前值的來源。 您隨時都可以使用 SetCurrentValue 您想要設定值,而不需提供該值的本機值優先順序。 例如,如果屬性是由觸發程式所設定,然後透過 SetCurrentValue 指派另一個值,則屬性系統仍然會遵守觸發程式,而且如果觸發程式的動作發生,屬性將會變更。 SetCurrentValue 可讓您變更屬性的值,而不給予屬性較高優先順序的來源。 同樣地,您可以使用 SetCurrentValue 來變更屬性值,而不覆寫系結。

強制型轉、動畫和基底值

強制和動畫都會針對整個 SDK 中稱為「基底值」的值採取行動。 因此,基底值是透過在項目中往上評估,直到達到第 2 個項目來決定的值。

對動畫而言,如果動畫未同時指定特定行為的 "From" 和 "To",或是動畫刻意在完成後還原成基底值,則基底值可能會對動畫值造成影響。 若要了解實際的情形,請執行 From、To 和 By 動畫目標值範例。 請試著設定此範例中矩形高度的區域數值,讓初始區域數值與動畫中的任何 "From" 不同。 您會發現,動畫會立即使用 "From" 值啟動,並在啟動後取代基底值。 動畫可以藉由指定 Stop FillBehavior 來指定在動畫完成之前找到的值。 在此之後,就會使用正常優先順序來決定基底值。

您可將多個動畫套用到單一屬性,每個動畫可能已透過值優先順序的不同點定義。 不過,這些動畫可能會將值結合起來,而不是只從較高的優先順序套用動畫。 這完全取決於動畫的定義方式,以及要顯示為動畫之值的類型。 如需將屬性顯示為動畫的詳細資訊,請參閱動畫概觀

強制型轉的套用層級最高。 即使是已在執行中的應用程式也受值強制型轉的限制。 WPF 中某些現有的相依性屬性具有內建強制。 針對自訂相依性屬性,您可以撰寫 CoerceValueCallback ,並在建立屬性時傳遞回呼作為中繼資料的一部分,以定義自訂相依性屬性的強制行為。 您也可以覆寫現有屬性的強制型轉行為,方式是在衍生類別中覆寫該屬性上的中繼資料。 強制型轉與基底值互動的方式是,強制型轉上的條件約束會以當時存在的條件約束套用,但仍保留基底值。 因此,如果強制型轉中的條件約束稍後放寬,強制型轉就會傳回最接近該基底值的值,而且強制型轉對屬性的影響有可能會在所有條件約束都放寬之後停止。 如需強制型轉行為的詳細資訊,請參閱相依性屬性回呼和驗證

觸發程序行為

控制項通常會將觸發程序行為定義為其在主題中之預設樣式的一部分。 在控制項上設定區域屬性,可能會使觸發程序無法從視覺或行為上回應使用者驅動的事件。 屬性觸發程式最常見的用法是控制項或狀態屬性,例如 IsSelected 。 例如,根據預設,當 Button 停用 時 (觸發 IsEnabled 程式 為 false ),則 Foreground 主題樣式中的值會讓控制項顯示為「灰色」。 但是,如果您已設定本機 Foreground 值,即使在此屬性觸發的案例中,一般灰色色彩也會優先于您的本機屬性設定。 當您為具有主題層級觸發程序行為的屬性設定值時,請特別小心,並確保不會不當干擾該控制項的預期使用者體驗。

ClearValue 和值優先順序

方法 ClearValue 提供一種快速的方法,可從專案上設定的相依性屬性清除任何本機套用的值。 不過,呼叫 ClearValue 不保證在屬性註冊期間中繼資料中建立的預設值是新的有效值。 值優先順序中的所有其他參與者仍在作用中。 只有在本機設定的值會從優先順序移除。 例如,如果您在屬性上呼叫 ClearValue 該屬性也是由主題樣式設定的屬性,則主題值會套用為新值,而不是以中繼資料為基礎的預設值。 如果您想要將所有屬性值參與者從處理常式中取出,並將值設定為已註冊的中繼資料預設值,您可以藉由查詢相依性屬性中繼資料來明確取得該預設值,然後使用預設值在本機設定屬性,並呼叫 SetValue

另請參閱