Loading

WPF依赖属性详解

前言

如果看文字版本不没耐心, 点击下方查看视频讲解。
点击观看视频

回顾

在讲解依赖属性之前, 首先我们熟悉一下WPF当中的绑定(Binding), 可能你曾用过WPF中绑定的语法。

下面演示了在Button按钮上为Content属性设置了一个绑定语法, 如下所示:

 <Button Content="{Binding Content}"/>

当你在Content属性按下F12转到定义时,可以观察到Button按钮所继承的类的定义,如下所示:

如图上红圈位置, 定义了一个静态的只读字段ContentProperty。
通过查看该字段的类型DependencyProperty,没错!它就是一个依赖属性。

看到这里, 你应该有了一个概念, 在WPF当中, 所有支持绑定的属性本质上它都是封装后的依赖属性。
那么也就是说, 只有依赖属性才可以进行绑定
如果你不能够理解, 我们再举个例子, 当你使用WPF当中PasswordBox控件的时候, 你会发现Password属性不支持绑定, 当按下F12转到定义,会发现该Password就是一个普通属性:

这也就是为什么Password不支持绑定的真正原因。

属性和依赖属性

现在, 我们来解决另外一个概念问题, 可能看到上面, 你还是不太清楚属性和依赖属性它们的区别在哪里?

属性

很常见, 在C#中的标准属性,通常会由一个非静态类型的私有字段支持, 假设当前有一个对象, 它拥有100个标准属性,
并且背后都定义了一个4字节的字段, 如果我们初始化10000个这样的对象, 那么这些字段将占用100×4×10000= 3.81M 内存。
但是实际上, 我们并非使用到所有的属性, 这就意味着大多数内存会被浪费!

依赖属性

如何解决属性带来的问题? 我们回到现实生活当中想象一种场景, 假设老王和你的女朋友去旅游, 他们准备东西的时候大都是把必要的带上, 而不是说女朋友想喝水,要不然在行李箱里面放一箱水? 那么是不是意味着上厕所把纸带上? 洗发水? 沐浴露? 天呐, 这真是一场糟糕的旅行。
我们都知道, 水、厕所纸、洗发水、沐浴露这些酒店里面都有阿, 为什么要我们自己带? 所以我们懂了, 这些不必要带的东西我们可以依赖外部提供给我们。是的, 我们把这种思想带到编程当中。
所以, 这就是WPF当中的依赖属性的理念, 也许你在其它的地方都听过别人讲解过依赖属性, 并且他们都告诉你依赖属性本身没有值, 可以依赖绑定来源获得值。

总结

下面是对比属性/依赖属性改的定义:

       //依赖属性

        public static readonly DependencyProperty MyPropertyProperty =
           DependencyProperty.Register("MyProperty", typeof(int), typeof(Test), new PropertyMetadata(0));

        public int MyProperty
        {
            get { return (int)GetValue(MyPropertyProperty); }
            set { SetValue(MyPropertyProperty, value); }
        }

        //属性

        private int name;

        public int Name
        {
            get { return name; }
            set { name = value; }
        }

通过上面我们可以了解到, 普通的属性通过定义了一个私有字段, 然后通过这个私有字段进行读取或设置值。
而对于依赖属性, 它同样是使用属性的访问方式, 但是它获取和设置值是通过依赖属性而不再是私有字段。

依赖属性定义

同样, 你也许在许多的博客当中, 别人教你怎么定义依赖属性, 它通过DependencyProperty类的静态Register方法注册, 如下所示:

    public class Test : DependencyObject
    {
        public string Message
        {
            get { return (string)GetValue(MyPropertyProperty); }
            set { SetValue(MyPropertyProperty, value); }
        }

        public static readonly DependencyProperty MyPropertyProperty =
            DependencyProperty.Register("Message", typeof(string), typeof(Test), new PropertyMetadata(null));
    }

上面的代码当中, 我们定义了一个string类型的依赖属性Message, 如果你们有看过WPF的源代码, 你可能会了解, 在其背后, 生成了一个key/value存储在Hashtable里面。

  • 生成key的代码片段
  • 添加到Hashtable中

    终于解开了疑惑,有了这个全局的Hashtable存在, 意味着,他就是用来存储依赖属性实例的地方。

注意:定义依赖属性的地方, 你会发现它需要继承于DependencyObject对象, 通过该对象当中的提供的GetValue/SetValue方法, 我们可以获取/设置依赖属性的值:

GetValue/SetValue哪里来?

看看WPF中的继承关系, 噢,懂了。 基类就是DependencyObject。

噢! 我的🐎🦆, 这也就是为什么, 微软提供了标准属性的访问方式!让我们傻傻分不清其内部通过依赖属性来设置或获取值。

在线搜索WPF源代码

附加属性

字母意思来理解, 附加属性就是对于一个对象而言, 本来它不具备这个属性, 但是由于附加给这个对象, 然后才有了这个属性,这种我们称之为附加属性。
:附加属性也是依赖属性, 只是它的注册方式与表达方式略有不同。
如果仍然不理解, 我们可以找到WPF当中经常用到的例子, 如下所示, 当一个按钮处在不同的容器当中, 它就具有了不同的附加属性:

        <Grid>
            <!--在Grid当中,具备Row/Column附加属性-->
            <Button Grid.Row="0" Grid.Column="0" />
        </Grid>
       
        <DockPanel>
            <!--在DockPanel中,具备Dock附加属性-->
            <Button DockPanel.Dock="Left" />
        </DockPanel>

        <Canvas>
            <!--在Canvas中,具备Left/Top/Rifht/Bottom等附加属性-->
            <Button Canvas.Left="10"/>
        </Canvas>

依赖属性与附加属性对比

声明方式

创建依赖属性

我们可以输入propdp再按两下tab键生成一个依赖属性的模板,如下所示:
: 声明依赖属性的所在位置的对象必须直接或简介继承于DependencyObject对象, 这样它才具备GetValue/SetValue方法。

        public int MyProperty
        {
            get { return (int)GetValue(MyPropertyProperty); }
            set { SetValue(MyPropertyProperty, value); }
        }

        // Using a DependencyProperty as the backing store for MyProperty.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty MyPropertyProperty =
            DependencyProperty.Register("MyProperty", typeof(int), typeof(ownerclass), new PropertyMetadata(0));

创建附加属性

我们可以输入propa再按两下tab键生成一个附加属性的模板,如下所示:
:声明附加属性的对象无需继承于DependencyObject, 因为这个时候DependencyObject对象作为方法参数传递。

        public static int GetMyProperty(DependencyObject obj)
        {
            return (int)obj.GetValue(MyPropertyProperty);
        }

        public static void SetMyProperty(DependencyObject obj, int value)
        {
            obj.SetValue(MyPropertyProperty, value);
        }

        // Using a DependencyProperty as the backing store for MyProperty.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty MyPropertyProperty =
            DependencyProperty.RegisterAttached("MyProperty", typeof(int), typeof(ownerclass), new PropertyMetadata(0));

使用场景

依赖属性: 当您需要单独创建控件时, 并且希望控件的某个部分能够支持数据绑定时, 你则可以使用到依赖属性。
附加属性: 这种情况很多, 正因为WPF当中并不是所有的内容都支持数据绑定, 但是我们希望其支持数据绑定, 这样我们就可以创建基于自己声明的附加属性,添加到元素上, 让其元素的某个原本不支持数据绑定的属性间接形成绑定关系。
例如:为PassWord定义附加属性与PassWord进行关联。例如DataGrid控件不支持SelectedItems, 但是我们想要实现选中多个条目进行数据绑定, 这个时候也可以声明附加属性的形式让其支持数据绑定。

posted @ 2021-01-29 17:32  痕迹g  阅读(14193)  评论(10编辑  收藏  举报