WPF学习笔记06-依赖属性DependencyProperty

在学习依赖属性之前,我们首先要知道属性是什么?属性呢其实就是为了保护数据!避免数据直接暴漏给外界。什么是依赖属性呢?依赖属性和属性又有什么区别呢?依赖属性是一种可以自己没有值,并能通过使用Binding从数据源获得值的属性。言简意赅,就是依靠着别人赋值的属性。至于区别是什么,我们接下来说完依赖属性之后也许你就会多多少少理解一点了!

1 - 了解依赖属性

首先,我们需要先了解下依赖属性,依赖属性和普通属性的用法其实是一样的,在之前的Demo中我们每个Demo都有用到依赖属性只不过你不知道而已,比如Background="Blue"这个Background就是一个属性并且是一个DependencyProperty也就是依赖属性。

依赖属性其实就是专门针对WPF而创建的。但是呢,我们使用的时候会没有任何感觉,是因为WPF库都使用了普通属性进行了封装,这个之后我们也会介绍到一个概念(包装器)所以我们也能够常规的去使用它了。其实当我们回头看我们写过的代码的时候,每个属性F12之后你都能看到对应的声明基本上都包含了一个词DependencyProperty

可能现在只通过文字去描述依赖属性你还是不太理解。那咱们接下来往下看。

2 - 使用依赖属性

2.1 - 定义依赖属性

其实依赖属性在我们实际开发过程中,很多时候我们都仅仅是使用它,但是对于一些特殊的场景比如我们创建的自定义用户控件等情况下我们还是会要用到的。

首先定义依赖属性的前提是,对应的对象也就是所属类必须继承于DependencyObject依赖对象。在WPF中我们使用的时候没感觉是因为大多数你一直F12之后最后都能发现继承于DependencyObject。

依赖属性定义:

public static readonly DependencyProperty xxxxProperty;

依赖属性的结尾根据WPF定义的规则必须是以Property结尾的,且需要为静态只读。

2.2 - 注册依赖属性

注册依赖函数,需要在任何可能使用到依赖属性之前完成,因此必须要在关联的静态构造函数中进行。在WPF的机制当中,DependencyProperty是不能被直接实例化的。因为DependencyProperty没有公有构造函数。只能通过DependencyProperty.Regsiter()方法进行注册从而完成实例。并且还需要确认一件事就是创建后不被改变该对象这也就是为什么所有的都是是只读的。

注册依赖属性

TestProperty =
            DependencyProperty.Register("MyProperty", typeof(int), typeof(MainWindow), new PropertyMetadata(0));

调用Register时需要传入几个参数

1)属性名

2)属性使用的类型

3)拥有该属性的所属类型

4)PropertyMetadata对象【可选】

5)验证属性的回调函数ValidateValueCallback【可选】

前三个必填项比较显而易见,不过多赘述,接下来介绍下PropertyMetadata和ValidateValueCallback

2.2.1 - PropertyMetadata

PropertyMetadata一共有五个重载

public PropertyMetadata()
{
}

public PropertyMetadata(object defaultValue)
{
DefaultValue = defaultValue;
}

public PropertyMetadata(PropertyChangedCallback propertyChangedCallback)
{
PropertyChangedCallback = propertyChangedCallback;
}

public PropertyMetadata(object defaultValue, PropertyChangedCallback propertyChangedCallback)
{
DefaultValue = defaultValue;
PropertyChangedCallback = propertyChangedCallback;
}

public PropertyMetadata(object defaultValue, PropertyChangedCallback propertyChangedCallback, CoerceValueCallback coerceValueCallback)
{
DefaultValue = defaultValue;
PropertyChangedCallback = propertyChangedCallback;
CoerceValueCallback = coerceValueCallback;
}

1) 空的

2)默认值【类型需和所声明的类型一致】

new PropertyMetadata(默认值【类型需要和所声明的一直】)

3)PropertyChangedCallback

propertyMetadata.PropertyChangedCallback = ((s, e) =>
{
    Debug.WriteLine(String.Format("PropertyChanged - 属性:{0} 新值:{1} 旧值:{2}", e.Property.Name, e.NewValue, e.OldValue));
});

4) CoerceValueCallback

propertyMetadata.CoerceValueCallback = (s, e) =>
{
    Debug.WriteLine(String.Format("CoerceValue - {0}", e));
    return e;
};

2.2.2 - ValidateValueCallback

(o) => {
    Brush brush = o as Brush;
    if (brush== Brushes.Yellow||brush== Brushes.Blue)
    {
        return true;
    }
    else
    {
        return false;
    }
}

比如上边判断如果为黄色或者蓝色才可以,其他颜色都不可以。如果我们前台赋值为Orange时则会提示

在接下来我们会着重介绍对应的使用方式

2.3 - 属性包装器

当我们创建完成之后,最后一部就是用传统的.Net属性封装的方法进行封装。但是唯一不同的是依赖属性使用的是GetValue()和SetValue()方法

//属性包装器
public Brush MyColor
{
    get { return (Brush)GetValue(MyColorProperty); }
    set { SetValue(MyColorProperty, value); }
}

之后的话,我们使用依赖属性就和使用普通属性那样去使用就行了。

3 - 依赖属性优先级

依赖属性依赖于多个属性提供者。每个提供者都有对应的优先级。对应的优先级顺序【由低到高】如下

    1. 默认值(也就是PropertyMetadata中的defaultValue)
    2. 继承而来的值
    3. 主题样式的值
    4. 项目样式的值
    5. 本地值

WPF中按照对应优先级确定依赖属性的基本知识,但是基本知识不一定就是最后从属性中检索到的值,因为WPF还需要考虑一些其他因素。

WPF中决定属性值的步骤(四部)

1)确认基本值

2)如果属性是使用表达式设置的,就怼表达式进行求值。当前支持两类表达式【数据绑定】【资源】

3)如果属性是动画目标就应用动画

4)运行强制转换回调函数来修正属性值

4 - 附加属性

附加属性就是依赖属性的一个附加。比如Grid容器分为行和列,当我们分别往对应行列放置控件时。需要设置对应的Grid.Row和Grid.Column。其中Row和Column就是对应的附加属性。

自定义的附加依赖属性和依赖属性唯一区别就是对应的注册方式不一样。依赖属性调用的是Register,附加依赖属性是用的RegisterAttached。

我们直接上代码

//声明
public static readonly DependencyProperty MyBgColorProperty;
static MyButton()
{
     MyBgColorProperty =
                DependencyProperty.RegisterAttached("MyBgColor", typeof(Brush), typeof(MyButton), propertyMetadata);
}
//属性包装器
public Brush MyBgColor
{
    get { return (Brush)GetValue(MyBgColorProperty); }
    set { SetValue(MyBgColorProperty, value); }
}

界面使用

<UserControl
    x:Class="FourthWPFDemo.MyButton"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:local="clr-namespace:FourthWPFDemo"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    Name="uc"
    d:DesignHeight="450"
    d:DesignWidth="800"
    mc:Ignorable="d">
    <Grid>
        <Button
            Background="{Binding ElementName=uc, Path=MyColor}"
            BorderBrush="{Binding ElementName=uc, Path=MyBgColor}"
            BorderThickness="3" />
    </Grid>
</UserControl>
<Window
    x:Class="FourthWPFDemo.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:local="clr-namespace:FourthWPFDemo"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:system="clr-namespace:System;assembly=mscorlib"
    Title="MainWindow"
    Width="800"
    Height="450"
    mc:Ignorable="d">
    <StackPanel>
        <local:MyButton
            Width="120"
            Height="30"
            Margin="0,30"
            MyColor="LightSkyBlue">
            <local:MyButton.MyBgColor>
                <SolidColorBrush Color="Pink" />
            </local:MyButton.MyBgColor>
        </local:MyButton>
    </StackPanel>
</Window>

至此,依赖属性的简单使用基本就学习完了

posted @ 2023-03-14 15:25  IceAmos  阅读(271)  评论(0编辑  收藏  举报