Windows Presentation Foundation(WPF)的可扩展应用程序标记语言(XAML)处理器实现本质上支持依赖属性。 因此,XAML 处理器使用 WPF 属性系统方法(如GetValue 和 SetValue)加载 XAML 和处理依赖属性的属性,并完全绕过依赖属性包装器。 因此,如果将自定义逻辑添加到自定义依赖属性的属性包装器,则当在 XAML 中设置属性值时,XAML 处理器不会调用它。
先决条件
本文假设对依赖属性有一个基本的了解,并且你已阅读 依赖项属性概述。 若要遵循本文中的示例,如果熟悉可扩展应用程序标记语言(XAML),并且知道如何编写 WPF 应用程序,则很有帮助。
WPF XAML 加载程序性能
为了设置依赖属性的值,WPF XAML 处理器直接调用 SetValue 比使用依赖属性的属性包装器在计算上代价更低。
如果 XAML 处理器确实使用属性包装器,则需要仅根据标记中指示的类型和成员关系推断支持代码的整个对象模型。 尽管可以通过组合使用 xmlns
和程序集属性从标记中识别类型,但要识别成员、确定哪些成员可以设置为属性,以及解析支持的属性值类型,仍然需要广泛使用 PropertyInfo 反射。
WPF 属性系统维护在给定 DependencyObject 派生类型上实现的依赖属性的存储表。 XAML 处理器使用该表来推断依赖属性的依赖属性标识符。 例如,按照约定,命名 ABC
的依赖属性的依赖属性标识符为 ABCProperty
。 XAML 处理器可以在其所含类型上使用依赖属性标识符调用 SetValue
方法,从而有效地设置任何依赖属性的值。
有关依赖属性包装器的详细信息,请参阅 自定义依赖属性。
自定义依赖项属性的含义
WPF XAML 处理器绕过属性包装器,并直接调用 SetValue 以设置依赖属性值。 因此,请避免将任何额外的逻辑放在自定义依赖属性的访问器中 set
,因为当在 XAML 中设置属性值时,该逻辑不会运行。 访问器set
中应仅包含对SetValue
的调用。
同样,获取属性值的 WPF XAML 处理器的各个方面绕过属性包装器并直接调用 GetValue。 因此,也避免将任何额外的逻辑放在自定义依赖属性的访问器中 get
,因为该逻辑在 XAML 中读取属性值时不会运行。 访问 get
器应仅包含调用 GetValue
。
带包装器的依赖属性示例
以下示例显示了具有属性封装器的推荐依赖属性定义。 依赖属性标识符存储为 public static readonly
字段,并且 get
访问 set
器不包含除依赖属性值所需的 WPF 属性系统方法之外的代码。 如果代码需要在依赖属性的值发生更改时运行,请考虑将该代码 PropertyChangedCallback 放入依赖属性中。 有关更多信息,请参阅 属性变更回调。
// Register a dependency property with the specified property name,
// property type, owner type, and property metadata. Store the dependency
// property identifier as a public static readonly member of the class.
public static readonly DependencyProperty AquariumGraphicProperty =
DependencyProperty.Register(
name: "AquariumGraphic",
propertyType: typeof(Uri),
ownerType: typeof(Aquarium),
typeMetadata: new FrameworkPropertyMetadata(
defaultValue: new Uri("http://www.contoso.com/aquarium-graphic.jpg"),
flags: FrameworkPropertyMetadataOptions.AffectsRender,
propertyChangedCallback: new PropertyChangedCallback(OnUriChanged))
);
// Property wrapper with get & set accessors.
public Uri AquariumGraphic
{
get => (Uri)GetValue(AquariumGraphicProperty);
set => SetValue(AquariumGraphicProperty, value);
}
// Property-changed callback.
private static void OnUriChanged(DependencyObject dependencyObject,
DependencyPropertyChangedEventArgs e)
{
// Some custom logic that runs on effective property value change.
Uri newValue = (Uri)dependencyObject.GetValue(AquariumGraphicProperty);
Debug.WriteLine($"OnUriChanged: {newValue}");
}
' Register a dependency property with the specified property name,
' property type, owner type, and property metadata. Store the dependency
' property identifier as a public static readonly member of the class.
Public Shared ReadOnly AquariumGraphicProperty As DependencyProperty =
DependencyProperty.Register(
name:="AquariumGraphic",
propertyType:=GetType(Uri),
ownerType:=GetType(Aquarium),
typeMetadata:=New FrameworkPropertyMetadata(
defaultValue:=New Uri("http://www.contoso.com/aquarium-graphic.jpg"),
flags:=FrameworkPropertyMetadataOptions.AffectsRender,
propertyChangedCallback:=New PropertyChangedCallback(AddressOf OnUriChanged)))
' Property wrapper with get & set accessors.
Public Property AquariumGraphic As Uri
Get
Return CType(GetValue(AquariumGraphicProperty), Uri)
End Get
Set
SetValue(AquariumGraphicProperty, Value)
End Set
End Property
' Property-changed callback.
Private Shared Sub OnUriChanged(dependencyObject As DependencyObject,
e As DependencyPropertyChangedEventArgs)
' Some custom logic that runs on effective property value change.
Dim newValue As Uri = CType(dependencyObject.GetValue(AquariumGraphicProperty), Uri)
Debug.WriteLine($"OnUriChanged: {newValue}")
End Sub