深入了解 |WPF的DependencyProperty

发布时间:2023年12月19日

今天我们将深入了解WPF(Windows Presentation Foundation)的一个核心概念:DependencyProperty。DependencyProperty是最能解释WPF特性的重要元素之一,在本文中,我们将简称其为DP。

DependencyProperty概览

DP的目的是将普通属性以静态方式注册并使用。在WPF中,大多数UI属性都使用DependencyProperty,这使得可以实现可绑定的属性特性。这样的属性例如ToggleButton的IsChecked。这些属性通过DependencyProperty.Register方法进行注册,在构造过程中由内部的DependencyProperty实例进行管理。

public static readonly DependencyProperty IsCheckedProperty =
    DependencyProperty.Register(
        "IsChecked",
        typeof(bool?),
        typeof(ToggleButton),
        new FrameworkPropertyMetadata(...,
            new PropertyChangedCallback(OnIsCheckedChanged)));

public bool? IsChecked
{
    get => ...
    set => ...
}

private static void OnIsCheckedChanged(DependencyObject d,    
    DependencyPropertyChangedEventArgs e)
{
    ToggleButton button = (ToggleButton)d;
    bool? oldValue = (bool?) e.OldValue;
    bool? newValue = (bool?) e.NewValue;
    ...
}

DependencyProperty的结构

查看DependencyProperty的结构,它主要由三个元素组成:

  • 静态的DependencyProperty(通过Register进行注册)
  • bool? IsChecked(属性特性)
  • 静态的OnIsCheckedChanged(回调方法)

在这里,回调方法是可选的,根据使用目的可实现或不实现。

Register注册

由于DependencyProperty类是通过sealed访问修饰符封闭的,并且构造函数也被设置为private,因此只能在Register方法内部创建DP实例。这样的限制意味着创建DP的唯一方法是通过DependencyProperty.Register(或Attached)调用。在构造过程中,它会将自身添加到名为RegisteredPropertyList的静态集合对象中,从而能自然地收集并管理所有DP。

Property声明

使用DP的属性看起来像普通属性,但其内部的get和set没有实际使用的字段。相反,它们使用GetValue和SetValue方法。

public bool IsChecked
{
    get => (bool)GetValue(ContentNameProperty);
    set => SetValue(ContentNameProperty, value);
}

GetValue()和SetValue()方法可以通过DependencyObject使用。所有的WPF UI类都继承自DependencyObject,因此可以在任何地方注册DP并配置属性。

内部字段管理

实际上,在注册DP时,内部字段的管理已经准备好了。因此,归根结底,DependencyProperty内部管理一个(静态的)字段,并通过GetValue/SetValue方法提供字段的使用,然后通过属性来使用这个值。

依赖属性的概念

DP是通过DependencyProperty和DependencyObject以及内部逻辑,将所有的值以静态方式进行管理。这样以静态方式管理值的原因是,当DP自身没有值时,它会从VisualTree的层次结构中,比自己位置更高的最近的直接父控件继承DP值。由于这样的结构,可以期望在内存管理方面具有效率,并且对性能也有好处。

举个例子,假设在以下的WPF控件中使用了FontSize DP属性。

<Window FontSize="15">
    <StackPanel FontSize="15">
        <Button FontSize="15"/>
        <TextBlock FontSize="15"/>
        <MyCustomControl FontSize="15"/>
    </Grid>
</Window>


如果除了Window之外的所有控件都移除了FontSize属性,情况就会发生变化。

<Window FontSize="15">
    <StackPanel>
        <Button/>
        <TextBlock/>
        <MyCustomControl/>
    </Grid>
</Window>

这样的设计使得WPF控件在不同层级之间能够更有效地共享和继承属性值,同时还能减少冗余和提高性能。

在这种情况下,内部实际上只会有一个Window FontSize的值,而不是原来的5个。那么其他四个控件的FontSize值会处于Empty状态吗?答案是不会。因为DP会继承其父控件的值,所以从static Field继承的DP属性会表现得像是有自己的值一样。这就是依赖属性(DependencyProperty)的核心概念。

因此,在WPF中,所有控件都具有DataContext DP属性。除非显式更改了自己的DataContext,否则它们会使用其上级控件的DataContext。这样一来,只需在上级控件中设置一次DataContext,子控件就会自动参考并使用上级控件的DataContext。正是由于这样实现的依赖属性(DP)功能,WPF中的数据绑定能够如此强大地工作。

这种设计不仅简化了数据绑定和属性设置的逻辑,还使得在WPF应用程序的整体架构中,从上至下都能更加高效和灵活地管理状态和数据流,大大提高了开发效率和运行性能。

<Window DataContext="{Binding MyViewModel}">
    <StackPanel>
        <Button Command="{Binding MyCommand}"/>
        <TextBlock Text="{Binding MyText}"/>
        <MyCustomControl SomeProperty="{Binding MyProperty}"/>
    </Grid>
</Window>

在上面的例子中,StackPanel、Button、TextBlock、MyCustomControl都会引用其上级控件,即Window的DataContext。因此,在各个控件中无需单独设置DataContext,就能实现与ViewModel中的MyCommand、MyText、MyProperty的绑定。

依赖属性(DependencyProperty,简称DP)具有诸如继承、提供默认值、更改通知、数据绑定等特性,这些特性都极大地贡献了WPF的可用性和性能提升。希望通过本文,大家能理解DependencyProperty的基本概念、结构和工作原理。如果大家想了解更深入的内容,建议查找与DependencyProperty相关的资料,这将对大家有所帮助。
?

文章来源:https://blog.csdn.net/VickyQu214/article/details/132769046
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。