屬性值繼承 (WPF .NET)
屬性值繼承是 Windows Presentation Foundation (WPF) 屬性系統的功能,並適用於相依性屬性。 屬性值繼承可讓樹狀結構中的子元素從最接近的父元素取得特定屬性的值。 因為父元素可能也會透過屬性值繼承來取得其屬性值,因此系統有可能會不停地遞迴到頁面根元素。
WPF 屬性系統預設不會啟用屬性值繼承,除非特別在相依性屬性中繼資料中啟用,否則值繼承將處於非使用中狀態。 即使啟用屬性值繼承,子元素只會在缺少較高優先順序值的情況下繼承屬性值。
必要條件
本文假設您具備相依性屬性的基本知識,而且已閱讀相依性屬性概觀。 若要遵循本文中的範例,建議您先熟悉 Extensible Application Markup Language (XAML),並了解如何撰寫 WPF 應用程式。
透過元素樹狀結構繼承
屬性值繼承與物件導向程式設計中的類別繼承概念不同,其中衍生類別會繼承基底類別成員。 雖然在 XAML 中,繼承的基底類別屬性會公開為代表衍生類別之 XAML 元素的屬性,但這種繼承在 WPF 中也同樣為作用中的狀態。
屬性值繼承是相依性屬性值從父元素散佈到包含屬性之元素樹狀結構內子元素的機制。 在 XAML 標記中,元素的樹狀結構會顯示為巢狀項目。
下列範例將示範 XAML 中的巢狀專案。 WPF 會在 UIElement 類別上使用屬性中繼資料註冊 AllowDrop 相依性屬性,該中繼資料可啟用屬性值繼承,並會將預設值設定為 false
。 AllowDrop
相依性屬性存在於 Canvas、StackPanel和 Label元素上 ,因為這些都是衍生自 UIElement
。 由於 canvas1
上的 AllowDrop
相依性屬性已設為 true
,因此子代 stackPanel1
和 label1
元素會繼承 true
為其 AllowDrop
值。
<Canvas x:Name="canvas1" Grid.Column="0" Margin="20" Background="Orange" AllowDrop="True">
<StackPanel Name="stackPanel1" Margin="20" Background="Green">
<Label Name="label1" Margin="20" Height="40" Width="40" Background="Blue"/>
</StackPanel>
</Canvas>
您也可以透過程序設計方式建立元素的樹狀結構,方法是將元素物件新增至另一個元素物件的子元素集合。 在執行階段,屬性值繼承會在結果物件樹狀結構上運作。 在下列範例中,stackPanel2
會被新增至 canvas2
的子集合。 同樣地,label2
會被新增至 stackPanel2
的子集合。 由於 canvas1
上的 AllowDrop
相依性屬性已設為 true
,因此子代 stackPanel1
和 label1
元素會繼承 true
為其 AllowDrop
值。
Canvas canvas2 = new()
{
AllowDrop = true
};
StackPanel stackPanel2 = new();
Label label2 = new();
canvas2.Children.Add(stackPanel2);
stackPanel2.Children.Add(label2);
Dim canvas2 As New Canvas With {
.AllowDrop = True
}
Dim stackPanel2 As New StackPanel()
Dim label2 As New Label()
canvas2.Children.Add(stackPanel2)
stackPanel2.Children.Add(label2)
屬性值繼承的實際應用
特定的 WPF 相依性屬性預設會啟用值繼承,例如 AllowDrop 和 FlowDirection。 一般而言,預設啟用值繼承的屬性會在基底 UI 元素類別上實作,因此存在於衍生類別中。 例如,由於 AllowDrop
是在 UIElement 基底類別上實作,因此相依性屬性也會存在於衍生自 UIElement
的每個控制項中。 WPF 可讓使用者在父元素上設定屬性值一次,並讓該屬性值散佈至元素樹狀結構中的子系元素,而 WPF 可對相依性屬性啟用值繼承。
屬性值繼承模型會根據 相依性屬性值優先順序,指派繼承和未繼承的屬性值。 因此,只有在子元素屬性沒有較高的優先順序值 (例如本機設定值,或透過樣式、範本或資料繫結取得的值) 的情況下,父元素屬性值才會套用至子元素。
FlowDirection 相依性屬性會設定父元素內文字和子 UI 元素的配置方向。 一般而言,您可以預期頁面內的文字和 UI 元素流程方向一致。 由於值繼承是在 FlowDirection
的屬性中繼資料中啟用,因此值只需要在頁面的項目樹狀結構頂端設定一次。 在極少數情況下,混合流程方向可適用於頁面,您可以藉由指派本機設定值,在樹狀結構中的元素上設定不同流程方向。 新的流程方向接著會散佈到該層級以下的子系元素。
讓自訂屬性成為可繼承
您可以在的 FrameworkPropertyMetadata 的執行個體中啟用 Inherits 屬性,然後向該中繼資料執行個體註冊您的自訂相依性屬性,讓自訂相依性屬性成為可繼承。 依預設,在 FrameworkPropertyMetadata
中 Inherits
會設為 false
。 將屬性值設為可繼承會影響效能,因此建議只有在需要該功能時再將 Inherits
設為 true
。
當您在中繼資料中註冊已啟用 Inherits
的相依性屬性時,請使用 RegisterAttached 方法,如註冊附加屬性中所述。 此外,請將預設值指派給屬性,讓可繼承的值存在。 您也可以在擁有者類型上建立具有 get
和 set
存取子的屬性包裝函式,就像您針對非附加的相依性屬性所做的一樣。 如此一來,您就可以在擁有者或衍生類型上使用屬性包裝函式來設定屬性值。 下列範例會建立名為 IsTransparent
的相依性屬性,並已啟用 Inherits
且預設值為 false
。 此範例也包含具有 get
和 set
存取子的屬性包裝函式。
public class Canvas_IsTransparentInheritEnabled : Canvas
{
// Register an attached dependency property with the specified
// property name, property type, owner type, and property metadata
// (default value is 'false' and property value inheritance is enabled).
public static readonly DependencyProperty IsTransparentProperty =
DependencyProperty.RegisterAttached(
name: "IsTransparent",
propertyType: typeof(bool),
ownerType: typeof(Canvas_IsTransparentInheritEnabled),
defaultMetadata: new FrameworkPropertyMetadata(
defaultValue: false,
flags: FrameworkPropertyMetadataOptions.Inherits));
// Declare a get accessor method.
public static bool GetIsTransparent(Canvas element)
{
return (bool)element.GetValue(IsTransparentProperty);
}
// Declare a set accessor method.
public static void SetIsTransparent(Canvas element, bool value)
{
element.SetValue(IsTransparentProperty, value);
}
// For convenience, declare a property wrapper with get/set accessors.
public bool IsTransparent
{
get => (bool)GetValue(IsTransparentProperty);
set => SetValue(IsTransparentProperty, value);
}
}
Public Class Canvas_IsTransparentInheritEnabled
Inherits Canvas
' Register an attached dependency property with the specified
' property name, property type, owner type, and property metadata
' (default value is 'false' and property value inheritance is enabled).
Public Shared ReadOnly IsTransparentProperty As DependencyProperty =
DependencyProperty.RegisterAttached(
name:="IsTransparent",
propertyType:=GetType(Boolean),
ownerType:=GetType(Canvas_IsTransparentInheritEnabled),
defaultMetadata:=New FrameworkPropertyMetadata(
defaultValue:=False,
flags:=FrameworkPropertyMetadataOptions.[Inherits]))
' Declare a get accessor method.
Public Shared Function GetIsTransparent(element As Canvas) As Boolean
Return element.GetValue(IsTransparentProperty)
End Function
' Declare a set accessor method.
Public Shared Sub SetIsTransparent(element As Canvas, value As Boolean)
element.SetValue(IsTransparentProperty, value)
End Sub
' For convenience, declare a property wrapper with get/set accessors.
Public Property IsTransparent As Boolean
Get
Return GetValue(IsTransparentProperty)
End Get
Set(value As Boolean)
SetValue(IsTransparentProperty, value)
End Set
End Property
End Class
附加屬性在概念上類似於全域屬性。 您可以在任何 DependencyObject 上檢查其值,並取得有效的結果。 附加屬性的典型範例是在子元素上設定屬性值,而且如果有問題的屬性是會在樹狀結構的每個 DependencyObject 元素上隱含顯示為附加屬性,則該案例將更有效率。
跨樹狀結構界限繼承屬性值
屬性繼承的運作方式是周遊元素的樹狀結構。 此樹狀結構通常會與邏輯樹狀結構平行。 不過,每當您在定義元素樹狀結構的標記中加入 WPF 核心層級物件時 (例如 Brush),就表示您已建立不連續的邏輯樹狀結構。 真正的邏輯樹狀結構在概念上不會透過 Brush
來擴展,因為邏輯樹狀結構是一個 WPF 結構層級概念。 您可以使用 LogicalTreeHelper 的協助程式方法來分析和檢視邏輯樹狀結構的範圍。 屬性值繼承可以透過不連續的邏輯樹狀結構傳遞繼承值,但只有在可繼承的屬性註冊為附加屬性,而且沒有刻意的繼承封鎖界限時,例如 Frame。
注意
雖然屬性值繼承似乎適用於非附加的相依性屬性,但在執行階段的樹狀結構中,透過部分元素界限的非附加屬性繼承行為尚未定義。 當您在屬性中繼資料中指定 Inherits 時,請使用 RegisterAttached 註冊屬性。