相依性屬性概觀 (WPF .NET)

Windows Presentation Foundation (WPF) 提供一組可用來擴充類型的屬性功能之服務。 這些服務統稱為 WPF 屬性系統。 WPF 屬性系統所支援的屬性稱為相依性屬性。 本概觀描述 WPF 屬性系統和相依性屬性的功能,包括如何在 XAML 和程式碼中使用現有的相依性屬性。 本概觀也介紹相依性屬性的特製化層面,例如相依性屬性中繼資料,以及如何在自訂類別中建立您自己的相依性屬性。

重要

.NET 7 和 .NET 6 的桌面指南檔正在建置中。

必要條件

本文假設 .NET 類型系統和麵向物件程式設計的基本知識。 若要遵循本文中的範例,它有助於瞭解 XAML 並知道如何撰寫 WPF 應用程式。 如需詳細資訊,請參閱 教學課程:使用 .NET 建立新的 WPF 應用程式。

相依性屬性與 CLR 屬性

WPF 屬性通常會公開為標準 .NET 屬性 。 您可能會在基本層級與這些屬性互動,且永遠不知道它們已實作為相依性屬性。 不過,熟悉 WPF 屬性系統的某些或所有功能,將協助您利用這些功能。

相依性屬性的目的是要根據其他輸入的值來計算屬性值,例如:

  • 系統屬性,例如主題和使用者喜好設定。
  • Just-In-Time 屬性判斷機制,例如資料系結和動畫/分鏡腳本。
  • 多用途範本,例如資源和樣式。
  • 透過父子式關聯性與專案樹狀結構中其他元素已知的值。

此外,相依性屬性也可以提供:

  • 獨立式驗證。
  • 預設值。
  • 監視其他屬性變更的回呼。
  • 系統,可根據執行時間資訊強制屬性值。

衍生類別可以藉由覆寫相依性屬性的中繼資料來變更現有屬性的某些特性,而不是覆寫現有屬性的實際實作或建立新屬性。

在 SDK 參考中,您可以藉由該屬性之 Managed 參考頁面上的 [相依性屬性資訊] 區段來識別相依性屬性。 [相依性屬性資訊] 區段包含該相依性屬性識別碼 DependencyProperty 欄位的連結。 它也包含該屬性的中繼資料選項清單、每個類別的覆寫資訊和其他詳細資料。

相依性屬性支援 CLR 屬性

相依性屬性和 WPF 屬性系統藉由提供支援屬性的類型來擴充屬性功能,以替代使用私用欄位支援屬性的標準模式。 這個類型的名稱為 DependencyProperty。 另一個定義 WPF 屬性系統的重要類型是 DependencyObject ,它會定義可註冊及擁有相依性屬性的基類。

以下是一些常用的術語:

  • 相依性屬性 ,這是 由 支援 DependencyProperty 的屬性。

  • 相依性屬性識別碼 ,這是 DependencyProperty 註冊相依性屬性時取得為傳回值的實例,然後儲存為類別的靜態成員。 與 WPF 屬性系統互動的許多 API 都會使用相依性屬性識別碼做為參數。

  • CLR 「wrapper」 ,這是 get 屬性的 和 set 實作。 這些實作會在 和 SetValue 呼叫中使用 GetValue 它,併入相依性屬性識別碼。 如此一來,WPF 屬性系統就會提供 屬性的備份。

下列範例會 IsSpinning 定義相依性屬性,以顯示識別碼與其所支援的 屬性的關聯 DependencyProperty 性。

public static readonly DependencyProperty IsSpinningProperty = DependencyProperty.Register(
    "IsSpinning", typeof(bool),
    typeof(MainWindow)
    );

public bool IsSpinning
{
    get => (bool)GetValue(IsSpinningProperty);
    set => SetValue(IsSpinningProperty, value);
}
Public Shared ReadOnly IsSpinningProperty As DependencyProperty =
    DependencyProperty.Register("IsSpinning", GetType(Boolean), GetType(MainWindow))

Public Property IsSpinning As Boolean
    Get
        Return GetValue(IsSpinningProperty)
    End Get
    Set(value As Boolean)
        SetValue(IsSpinningProperty, value)
    End Set
End Property

屬性的命名慣例及其支援的 DependencyProperty 欄位很重要。 欄位名稱一律是屬性名稱,後綴尾碼 Property。 如需此慣例及其原因的詳細資訊,請參閱 自訂相依性屬性

設定屬性值

您可以在程式碼或 XAML 中設定屬性。

在 XAML 中設定屬性值

下列 XAML 範例會將按鈕的背景色彩設定為紅色。 XAML 屬性的字串值是由 WPF XAML 剖析器轉換成 WPF 類型的類型。 在產生的程式碼中,WPF 類型是 Color ,方法是 。 SolidColorBrush

<Button Content="I am red" Background="Red"/>

XAML 支援數種語法形式來設定屬性。 特定屬性要使用的語法取決於屬性所使用的實值型別,以及其他因素,例如類型轉換器的存在。 如需設定屬性的 XAML 語法詳細資訊,請參閱 WPF 中的 XAML 和 XAML 語法 詳細資料

下列 XAML 範例顯示另一個使用屬性元素語法而非屬性語法的按鈕背景。 XAML 不設定簡單的純色,而是將按鈕 Background 屬性設定為影像。 元素代表該影像,而巢狀元素的屬性會指定影像的來源。

<Button Content="I have an image background">
    <Button.Background>
        <ImageBrush ImageSource="stripes.jpg"/>
    </Button.Background>
</Button>

在程式碼中設定屬性

在程式碼中設定相依性屬性值通常只是呼叫 set CLR 「包裝函式」所公開的實作:

Button myButton = new();
myButton.Width = 200.0;
Dim myButton As New Button With {
    .Width = 200.0
}

取得屬性值基本上是「包裝函式」實作的 get 呼叫:

double whatWidth = myButton.Width;
Dim whatWidth As Double = myButton.Width

您也可以直接呼叫屬性系統 API GetValueSetValue 。 直接呼叫 API 適用于某些案例,但通常不是當您使用現有屬性時。 一般而言,包裝函式更方便,並且為開發人員工具提供更好的屬性曝光。

屬性也可以在 XAML 中設定,稍後再於程式碼中透過程式碼後置存取。 如需詳細資訊,請參閱 WPF 中的程式碼後置和 XAML。

相依性屬性提供的屬性功能

不同于欄位所支援的屬性,相依性屬性會擴充屬性的功能。 新增的功能通常代表或支援下列其中一項功能:

資源

您可以藉由參考資源來設定相依性屬性值。 資源通常會指定為 Resources 頁面根項目或應用程式的屬性值,因為這些位置提供方便的資源存取。 在此範例中,我們會定義 SolidColorBrush 資源:

<StackPanel.Resources>
    <SolidColorBrush x:Key="MyBrush" Color="Gold"/>
</StackPanel.Resources>

現在已定義資源,我們可以參考資源來提供 屬性的值 Background

<Button Background="{DynamicResource MyBrush}" Content="I am gold" />

在 WPF XAML 中,您可以使用靜態或動態資源參考。 此特定資源會參考為 DynamicResource 。 動態資源參考只能用來設定相依性屬性,因此它是 WPF 屬性系統所啟用的動態資源參考使用量。 如需詳細資訊,請參閱 XAML 資源

注意

資源會被視為本機值,這表示如果您設定另一個本機值,您將排除資源參考。 如需詳細資訊,請參閱 相依性屬性值優先順序

資料繫結

相依性屬性可以透過資料繫結參考值。 資料繫結的運作是透過 XAML 中的特定標記延伸語法,或程式碼中的 Binding 物件。 使用資料系結時,最終屬性值的判斷會延後到執行時間,此時該值會從資料來源取得。

下列範例會使用 XAML 中宣告的 Button 系結, Content 設定 的 屬性。 繫結使用繼承的資料內容和 XmlDataProvider 資料來源 (未顯示)。 系結本身會依 XPath 指定資料來源內的 source 屬性。

<Button Content="{Binding Source={StaticResource TestData}, XPath=test[1]/@text}"/>

注意

系結會被視為本機值,這表示如果您設定另一個本機值,您將排除系結。 如需詳細資訊,請參閱 相依性屬性值優先順序

相依性屬性或 類別 DependencyObject 原本就不支援 INotifyPropertyChanged 資料系結作業之來源屬性值變更 DependencyObject 的通知。 如需如何建立屬性以用於可報告資料系結目標變更的資料系結的詳細資訊,請參閱 資料系結概觀

樣式

樣式和範本是使用相依性屬性的令人信服的理由。 樣式特別適用于設定定義應用程式 UI 的屬性。 樣式通常會定義為 XAML 中的資源。 樣式會與屬性系統互動,因為它們通常包含特定屬性的 「setters」,而「觸發程式」會根據另一個屬性的運行時間值來變更屬性值。

下列範例會建立簡單的樣式,該樣式會在字典內 Resources 定義(未顯示)。 然後,該樣式會直接套用至 StyleButton 屬性。 樣式中的 setter 會將樣式 ButtonBackground 屬性設定為綠色。

<Style x:Key="GreenButtonStyle">
    <Setter Property="Control.Background" Value="Green"/>
</Style>
<Button Style="{StaticResource GreenButtonStyle}" Content="I am green"/>

如需詳細資訊,請參閱設定樣式和範本

動畫

相依性屬性可以動畫方式顯示。 當套用的動畫執行時,動畫值的優先順序高於任何其他屬性值,包括本機值。

下列範例會以動畫顯示 BackgroundButton 屬性。 從技術上看,屬性元素語法會將空白 SolidColorBrush 設定為 Background ,而 ColorSolidColorBrush 屬性會以動畫顯示。

<Button Content="I am animated">
    <Button.Background>
        <SolidColorBrush x:Name="AnimBrush"/>
    </Button.Background>
    <Button.Triggers>
        <EventTrigger RoutedEvent="FrameworkElement.Loaded">
            <BeginStoryboard>
                <Storyboard>
                    <ColorAnimation
                        Storyboard.TargetName="AnimBrush" 
                        Storyboard.TargetProperty="(SolidColorBrush.Color)"
                        From="Blue" To="White" Duration="0:0:1" 
                        AutoReverse="True" RepeatBehavior="Forever" />
                </Storyboard>
            </BeginStoryboard>
        </EventTrigger>
    </Button.Triggers>
</Button>

如需動畫屬性的詳細資訊,請參閱 動畫概觀 分鏡腳本概觀

中繼資料覆寫

當您衍生自原本註冊相依性屬性的類別時,您可以覆寫其中繼資料,以變更相依性屬性的特定行為。 覆寫中繼資料依賴識別碼, DependencyProperty 而且不需要重新實作 屬性。 中繼資料變更會由屬性系統原生處理。 每個類別可能會針對繼承自基類的所有屬性保留個別中繼資料,並依據每個類型。

下列範例會覆寫相依性屬性的 DefaultStyleKey 中繼資料。 覆寫這個特定相依性屬性的中繼資料是實作模式的一部分,可用來建立可從主題使用預設樣式的控制項。

public class SpinnerControl : ItemsControl
{
    static SpinnerControl() => DefaultStyleKeyProperty.OverrideMetadata(
            typeof(SpinnerControl),
            new FrameworkPropertyMetadata(typeof(SpinnerControl))
        );
}
Public Class SpinnerControl
    Inherits ItemsControl
    Shared Sub New()
        DefaultStyleKeyProperty.OverrideMetadata(GetType(SpinnerControl), New FrameworkPropertyMetadata(GetType(SpinnerControl)))
    End Sub
End Class

如需覆寫或存取相依性屬性中繼資料的詳細資訊,請參閱 覆寫相依性屬性 的中繼資料。

屬性值繼承

項目可以繼承物件樹狀結構中的父代相依性屬性值。

注意

所有相依性屬性的屬性值繼承行為不會全域啟用,因為繼承的計算時間會影響效能。 屬性值繼承通常只會在建議適用性的案例中啟用。 您可以查看 SDK 參考中該相依性屬性的 Dependency Property Information 區段,檢查相依性屬性是否繼承

下列範例顯示包含 屬性的 DataContext 系結,以指定系結的來源。 因此,子物件中的系結不需要指定來源,而且可以在父 StackPanel 物件中使用繼承的值 DataContext 。 或者,子物件可以直接在 中 Binding 指定自己的 DataContextSource ,而不使用繼承的值。

<StackPanel Canvas.Top="50" DataContext="{Binding Source={StaticResource TestData}}">
    <Button Content="{Binding XPath=test[2]/@text}"/>
</StackPanel>

如需詳細資訊,請參閱 屬性值繼承

WPF 設計工具整合

實作為相依性屬性的自訂控制項會與 Visual Studio 的 WPF 設計工具整合。 其中一個範例是能夠在 [屬性 ] 視窗中編輯直接和附加的相依性屬性 。 如需詳細資訊,請參閱 控制撰寫概觀

相依性屬性值優先順序

WPF 屬性系統中的任何屬性型輸入都可以設定相依性屬性的值。 相依性屬性值優先順序 存在,因此屬性如何以可預測的方式取得其值的各種案例。

注意

SDK 檔有時會在討論相依性屬性時,使用「本機值」或「本機設定值」一詞。 本機設定值是在程式碼中直接在物件實例上設定的屬性值,或做為 XAML 中的專案屬性。

下一個範例包含套用至 Background 任何按鈕屬性的樣式,但指定一個具有本機設定 Background 屬性的按鈕。 就技術上而言,該按鈕的 Background 屬性已設定兩次,但只會套用一個值,也就是優先順序最高的值。 本機設定值具有最高的優先順序,但執行中的動畫不存在於此。 因此,第二個按鈕會使用 屬性的 Background 本機設定值,而不是樣式 setter 值。 第一個按鈕沒有本機值,或優先順序高於樣式 setter 的其他值,因此會使用 屬性的 Background 樣式 setter 值。

<StackPanel>
    <StackPanel.Resources>
        <Style x:Key="{x:Type Button}" TargetType="{x:Type Button}">
            <Setter Property="Background" Value="Orange"/>
        </Style>
    </StackPanel.Resources>
    <Button>I am styled orange</Button>
    <Button Background="Pink">I am locally set to pink (not styled orange)</Button>
</StackPanel>

為什麼會有相依性屬性優先順序?

本機設定值優先于樣式 setter 值,可支援專案屬性的本機控制。 如需詳細資訊,請參閱 相依性屬性值優先順序

注意

WPF 元素上定義的許多屬性不是相依性屬性,因為相依性屬性通常只有在需要 WPF 屬性系統的功能時才實作。 這些功能包括資料系結、樣式、動畫、預設值支援、繼承、附加屬性和失效。

深入了解相依性屬性

  • 元件開發人員或應用程式開發人員可能會想要建立自己的相依性屬性來新增功能,例如資料系結或樣式支援,或無效和值強制支援。 如需詳細資訊,請參閱 自訂相依性屬性

  • 請考慮相依性屬性為公用屬性,可供任何具有實例存取權的呼叫端存取或探索。 如需詳細資訊,請參閱 相依性屬性安全性

  • 附加屬性是支援 XAML 特殊語法的屬性類型。 附加屬性通常沒有與 Common Language Runtime 屬性的 1:1 對應,而且不一定是相依性屬性。 附加屬性的主要目的是允許子專案將屬性值報告給父元素,即使父元素和子項目不包含該屬性做為類別成員清單的一部分也一樣。 其中一個主要案例是讓子項目通知父元素如何在 UI 中呈現它們。 如需範例,請參閱 DockLeft 。 如需詳細資訊,請參閱 附加屬性概觀

另請參閱