代码改变世界

CLR属性、依赖属性与附加属性(WPF)

2011-12-20 21:54  木木子  阅读(1154)  评论(1编辑  收藏  举报

笔记大概

  • CLR属性
  • 依赖属性
  • 附加属性

CLR属性

public class Human
{
    private int age;
 
    public int Age
    {
        get
        {
            return this.age;
        }
        set
        {
            if (value >= 0 && value <= 100)
            {
                this.age = value;
            }
            else
            {
                throw new OverflowException("Age overflow");
            }
        }
    }
}

这就是CLR属性,将private字段包装安全的访问。其实,CLR属性的编译结果是两个方法,仅仅是个语法糖衣。

依赖属性

在WPF中,推出了“依赖属性”这个概念。依赖属性自己没有值,通过Binding从数据源获得值的属性。拥有依赖属性的对象称为“依赖对象”。与CLR属性相比,优势:

  • 节省实例对内存的开销。
  • 属性值可以通过Binding依赖在其他对象上。

在WPF中,依赖对象的概念被DependencyObject类实现,依赖属性的概念由DependencyProperty类实现。DependencyObject具有GetValue和SetValue两个方法:

public class DependencyObject : DispatcherObject
{
    public object GetValue(DependencyProperty dp)
    {
        //...
    }
    public void SetValue(DependencyProperty dp, object value)
    {
        //...
    }
}

DependencyObject是WPF系统中相当底层的一个基类:

image

如图所示,WPF的所有UI控件都是依赖对象。

最简单的DependencyProperty Coding:

public class Student : DependencyObject
{
    public static readonly DependencyProperty NameProperty = DependencyProperty.Register("Name", typeof(string), typeof(Student));
}
<Window x:Class="Property.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="144" Width="318">
    <StackPanel>
        <TextBox x:Name="tb1" BorderBrush="Black" Margin="5"/>
        <TextBox x:Name="tb2" BorderBrush="Black" Margin="5"/>
        <Button Content="OK" Margin="5" Click="Button_Click"></Button>
    </StackPanel>
</Window>
private void Button_Click(object sender, RoutedEventArgs e)
{
    Student stu = new Student();
    stu.SetValue(Student.NameProperty, this.tb1.Text);
    this.tb2.Text = (string)stu.GetValue(Student.NameProperty);
}

下面展示“依赖功能”:

Binding binding = new Binding("Text") { Source = tb1 };
BindingOperations.SetBinding(stu, Student.NameProperty, binding);

为依赖属性添加一个CLR属性包装:

public class Student : DependencyObject
{
    public string Name
    {
        get
        {
            return (string)GetValue(NameProperty);
        }
        set
        {
            SetValue(NameProperty, value);
        }
    }
 
    public static readonly DependencyProperty NameProperty = DependencyProperty.Register("Name", typeof(string), typeof(Student));
}

有了CLR属性的包装,我们使用就感觉自然多了。

再把依赖属性升级下,添加SetBinding方法:

public class Student : DependencyObject
{
    public string Name
    {
        get
        {
            return (string)GetValue(NameProperty);
        }
        set
        {
            SetValue(NameProperty, value);
        }
    }
 
    public static readonly DependencyProperty NameProperty = DependencyProperty.Register("Name", typeof(string), typeof(Student));
 
    public BindingExpressionBase SetBinding(DependencyProperty dp, BindingBase binding)
    {
        return BindingOperations.SetBinding(this, dp, binding);
    }
}

附加属性

铁锰大哥是这样解释附加属性的:比如一个叫Human的类,可能会与学校有关,也可能与公司有关,那么,设计这样的Human类时,是不是需要把学校、班级、学好、公司、工号等等相关属性加入Human中呢。显然不是很合适,一旦需求改变,这类的实现就需要做改变。

回头去看看布局那笔记,会发现,在Grid中定位,会在某个Control中使用Grid.Column和Grid.Row这些属性,在Canvas中会用到Canvas.Top,DockPanel中是DockPanel.Dock。这些属性就是附加属性,并非定义在Control中。所以,附加属性的作用是,将属性与数据类型(宿主)解耦,让数据类型设计更加灵活。

下面来使用附加属性:

class Human : DependencyObject
{
 
}
 
class School : DependencyObject
{
    public static int GetGrad(DependencyObject obj)
    {
        return (int)obj.GetValue(GradProperty);
    }
 
    public static void SetGrad(DependencyObject obj, int value)
    {
        obj.SetValue(GradProperty, value);
    }
 
    // Using a DependencyProperty as the backing store for Grad.  This enables animation, styling, binding, etc...
    public static readonly DependencyProperty GradProperty =
        DependencyProperty.RegisterAttached("Grad", typeof(int), typeof(School), new UIPropertyMetadata(0));
}