WPF 中最大的亮点当属 Binding 了,.NET 给我们提供了足够多的属性来让我们非常方便地进行数据绑定等操作。但即使如此,依然会有想编写自己的属性来进行 Binding 等操作。
在 WPF 中,微软将属性的概念延伸了一下,推出了“依赖属性”这个概念。所谓依赖属性,就是自己本身没有值而值是依赖别人的值。
为什么要使用依赖属性呢?举个例子,一个 TextBlock 有上百个属性,但是基本上我们常用的就是 Text 属性,当你在使用构造函数创建这个 TextBlock 的时候,这个实例的控件就已经被定下来了,随之而来的是大量不必要的空间浪费。而依赖属性,依赖别人的值而设定自己的值,这节省了大量不必要的空间浪费,而且可以方便的进行数据绑定,所以我们才要用依赖属性。
如何创建一个依赖属性呢?代码如下:
public static readonly DependencyProperty ContentProperty = DependencyProperty.Register("Content", typeof(string), typeof(Test));
而 static 和 readonly 是必须的,所以即使你创建了一百个依赖对象,也仅占一个空间,当然这可能有点难以理解,静态的和只读的与我们正常使用依赖属性的情况完全不符,不过你可以暂时或略这里,这样写的原因不是本文想要讲的,我会在之后的博客里讲为什么这样写。
然后是 DependencyProperty ,这是依赖属性类,所以后面的 ContentProperty 就是这个对象了。这里有一个命名规则,就是依赖属性的对象名都已 Peoperty 结尾,这样就表明他是一个依赖属性,方便他人阅读和理解。
你会发现我们并不是用构造函数来创建依赖属性的,而是使用了 DependencyProperty 类中的一个静态方法,来创建一个依赖属性。该方法有多个重载,详细请参见 MSDN 文档,这里使用的是最简单的重载,有三个参数:第一个是一个 String 对象,值为给这个依赖属性绑定的 CLR 属性;第二个为 Type 类型,表明这个依赖属性存储的值是什么类型,注意是这个依赖属性存储的值,这个依赖属性的类型永远是 DependencyProperty ;第三个参数也是 Type 类型,表明拥有该依赖属性的类是哪一个,也可以说把这个依赖属性注册到哪个类里,所以用 Register 这个名字就显得合理了。
下面来看一个稍完整的示例:
首先创建一个 WPF 项目,并创建一个类,名为 Test 。MainWindow.xaml 中代码如下:
Test.cs 文件中的代码如下:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
namespace Blog_DependencyProperty
{
public class Test : DependencyObject
{
public static readonly DependencyProperty ContentProperty = DependencyProperty.Register("Content", typeof(string), typeof(Test));
public Test()
{
}
}
}
如何为其添加 Binding 呢?MainWindow.xaml.cs 代码如下 :
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace Blog_DependencyProperty
{
///
/// MainWindow.xaml 的交互逻辑
///
public partial class MainWindow : Window
{
Test test;
public MainWindow()
{
InitializeComponent();
test = new Test();
Binding binding = new Binding("Text") { Source = textBox };
BindingOperations.SetBinding(test, Test.ContentProperty, binding);
}
private void button_Click(object sender, RoutedEventArgs e)
{
MessageBox.Show((string)test.GetValue(Test.ContentProperty));
}
}
}
而后面给按钮添加的事件处理函数很好理解。利用从 DependencyObject 对象继承而来的 GetValue 和 SetValue 方法可以读取或设置依赖属性的值。注意 GetValue 的方法返回值是 object 类型,所以你需要手动转换为你需要的类型。SetValue 方法稍后再讲。
好了,运行一下程序,在 TextBox 中输入内容,点击按钮,就会显示出你输入的内容。
内容就请大家忽略吧,最近博主有点感情受挫。
好了,言归正传,你会发现这里并没有用到创建(或者说注册更合适)依赖属性时的第一个参数,是的,即使没有 CLR 属性,也一样可以完美运行。
那么为什么我们还要有 CLR 属性来封装这个依赖属性的值呢?读者可以试下:
textBox.Text = Test.ContentProperty;
textBox.Text = (string)Test.ContentProperty;
添加如下代码:
public string Content
{
get
{
return (string)GetValue(ContentProperty);
}
set
{
SetValue(ContentProperty, value);
}
}
在 MainWindow.xaml.cs 中,删掉按钮的那个事件处理函数,然后在构造函数中添加如下代码:
textBlock.SetBinding(TextBlock.TextProperty, new Binding("Content") { Source = test });
好了,来让我们运行下吧。什么?等等?还没实现 Inotifypropertychanged 接口?不管怎么样,先让我们运行看看吧。
没错,像这样创建的依赖属性可以直接作为数据源使用。上面这个例子已经证明了现在我们自己创建的这个依赖属性既可以作为数据目标也可以作为数据源。
好了,本篇博客就讲到这里,谢谢大家!欢迎大家批评指正。
Remember !You Make Luck !