首页 > 其他分享 >8.依赖属性

8.依赖属性

时间:2024-01-30 16:35:47浏览次数:17  
标签:依赖 PropertyMetadata DependencyProperty using public 属性

WPF的依赖属性系统,它是指WPF提供的一组服务,专门用来扩展WPF的属性功能,而受到这些服务支持的属性就称为依赖属性。

WPF的依赖属性系统对于开发者而言,几乎是感知不到的,它通过DependencyProperty类型的一些静态方法成员,提供一系列注册依赖属性或附加属性的功能,让我们可以向依赖属性系统注册属于我们自己写的依赖属性。

为了对比CLR普通属性与WPF的依赖属性的区别,直观的认知两者的概念,我们先来看看普通属性的定义

普通属性

private int length = 0;
public int Length
{
    get { return length; }
    set { length = value; }
}

CLR普通属性的本质是在内部定义了一个私有字段,然后通过属性包装器将内部私有定段公开出来,get表示读出私有字段的值,set表示写入值到私有字段。假如WPF控件上的某个属性就是这类的普通属性,那么我们要更新这个属性的值,就只能赋值,用不能采用WPF的绑定方式了,因为只有依赖属性才支持绑定。

依赖属性的定义

在.cs里,输入propdp,然后按下tab键,VS会自动帮我们输入以下代码:

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));

我们来一一分析一下上述代码。

首先是MyPropertyProperty成员,它被声明为DependencyProperty 类型,且用DependencyProperty的Register方法注册,而在注册的时候,传入了4个参数。

第一个参数“MyProperty”:这个MyProperty其实是一个类似普通属性包装器的名字。经过依赖属性系统注册后,将来MyProperty就代表了MyPropertyProperty依赖属性。

第二个参数typeof(int):表示这个MyPropertyProperty的数据类型,也就是我们在使用MyProperty时的数据类型,这里被声明成int型。注意这里要求传入数据类型的反射。

第三个参数typeof(ownerclass):表示当前这个MyPropertyProperty依赖属性是属于哪个类的,一般填写当前这个类型。

第四个参数new PropertyMetadata(0):表示传入一个PropertyMetadata属性元数据。这个PropertyMetadata定义了MyPropertyProperty依赖属性的默认值和回调函数。回调函数就是当属性值发生改变时要执行的逻辑代码。

其次是MyProperty成员,它由CLR属性包装器实现get和set,并且使用了GetValue 和 SetValue成员去读出和写入MyPropertyProperty依赖属性。

哪儿来的GetValue和SetValue?

DependencyObject类。这个类就定义了GetValue和SetValue,分别表示获取某个依赖属性的值和写入一个值到某个依赖属性。结论,我们要在某个类中自定义一个依赖属性,那么这个类一定要继承DependencyObject基类。

依赖属性的使用

创建一个新的用户控件,取名为Widget

<UserControl x:Class="HelloWorld.Controls.Widget"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:local="clr-namespace:HelloWorld.Controls"
             mc:Ignorable="d" 
             x:Name="userControl"
             FontSize="30"
             Foreground="#666666"
             BorderBrush="#8CDDCD"
             d:DesignHeight="450" 
             d:DesignWidth="800">
    <Border BorderBrush="{Binding ElementName=userControl,Path=BorderBrush}">
        <Border.Style>
            <Style TargetType="Border">
                <Setter Property="Padding" Value="10"/>
                <Setter Property="Background" Value="White"/>
                <Setter Property="BorderBrush" Value="#8CDDCD"/>
                <Setter Property="BorderThickness" Value="0 3 0 0"/>
                <Setter Property="Margin" Value="5"/>
                <Style.Triggers>
                    <Trigger Property="IsMouseOver" Value="True">
                        <Setter Property="Background" Value="#F7F9F9"/>
                    </Trigger>
                </Style.Triggers>
            </Style>
        </Border.Style>
        <Grid>
            <Grid.ColumnDefinitions>
                <ColumnDefinition/>
                <ColumnDefinition Width="auto"/>
            </Grid.ColumnDefinitions>
            <Grid.RowDefinitions>
                <RowDefinition/>
                <RowDefinition/>
            </Grid.RowDefinitions>
            <TextBlock Grid.Row="0" Grid.Column="0" Text="{Binding Value}" 
                       Foreground="{Binding ElementName=userControl,Path=Foreground}" 
                       FontSize="{Binding ElementName=userControl,Path=FontSize}" />
            <TextBlock Grid.Row="1" Grid.Column="0" Text="{Binding Title}" 
                       Foreground="{Binding ElementName=userControl,Path=Foreground}" 
                       FontSize="14" TextWrapping="Wrap"/>
            <TextBlock Grid.Row="0" Grid.Column="1" Text="{Binding Icon}" 
                       Foreground="{Binding ElementName=userControl,Path=BorderBrush}" 
                       FontSize="26" Grid.RowSpan="2" VerticalAlignment="Center"/>
        </Grid>    
    </Border>
</UserControl>

在这里,我们一共实例化了5个控件,最外层的Border控件用来做修饰,且它的边框颜色绑定了当前UserControl控件的边框颜色。Grid里面有3个TextBlock文字块控件,其中的前景色、字号也分别绑定了当前UserControl的属性。这样做的好处是,将来实例化这个Widget自定义用户控件时,我们就可以设置它的相关属性,从而改变内部的边框颜色、字体颜色和字体大小。

需要注意的是,3个TextBlock控件Text属性分别绑定了Value、Title、Icon三个属性,这三个属性就是我们要去自定义的依赖属性。

定义依赖属性

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 HelloWorld.Controls
{
    /// <summary>
    /// Widget.xaml 的交互逻辑
    /// </summary>
    public partial class Widget : UserControl
    {  
        public Widget()
        {
            InitializeComponent();
 
            DataContext = this;
        }
 
 
        public string Icon
        {
            get { return (string)GetValue(IconProperty); }
            set { SetValue(IconProperty, value); }
        }
 
        public static readonly DependencyProperty IconProperty =
            DependencyProperty.Register("Icon", typeof(string), typeof(Widget), new PropertyMetadata("☻"));
 
 
        public string Title
        {
            get { return (string)GetValue(TitleProperty); }
            set { SetValue(TitleProperty, value); }
        }
 
        public static readonly DependencyProperty TitleProperty =
            DependencyProperty.Register("Title", typeof(string), typeof(Widget), new PropertyMetadata("请输入标题"));
 
 
        public string Value
        {
            get { return (string)GetValue(ValueProperty); }
            set { SetValue(ValueProperty, value); }
        }
 
        public static readonly DependencyProperty ValueProperty =
            DependencyProperty.Register("Value", typeof(string), typeof(Widget), new PropertyMetadata("内容"));
    }
}

通过在C#后端输入propdp,再按下tab键,VS会自动创建依赖属性的定义模板代码,我们只需要修改模板中的属性类型、属性名、和注册依赖属性时的相关参数即可。如上述代码所示,这里分别注册了IconProperty、TitleProperty和ValueProperty三个依赖属性,并且将它们注册到Widget类型上。由于依赖属性天生具有属性通知功能,所以我们不必去实现INotifyPropertyChanged接口,只需要将当前类做为ViewModel传给Widget的DataContent,前端的控件就可以绑定Value、Title、Icon三个属性了。

使用自定义控件

<Window x:Class="HelloWorld.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:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:HelloWorld" 
xmlns:controls="clr-namespace:HelloWorld.Controls" mc:Ignorable="d" Background="LightGray" Title="WPF中文网 - www.wpfsoft.com" Height="350" Width="500"> <StackPanel> <StackPanel Orientation="Horizontal"> <controls:Widget Icon="¥" Title="本年度销售总额" Value="38452.21" Width="215" Height="100"/> <controls:Widget Icon="◈" Title="系统访问量" Value="9985" Foreground="#415767" BorderBrush="#87BEE4" Width="225" Height="110"/> </StackPanel> <StackPanel Orientation="Horizontal"> <controls:Widget Icon="◈" Title="系统访问量" Value="9985" Foreground="#495E26" BorderBrush="#C1E487" Width="235" Height="120"/> <controls:Widget Icon="㍿" Title="日本丰田汽车国际进出口贸易有限公司" Value="股票代码95568" Foreground="#4E3A55" BorderBrush="#CB87E4" FontSize="24" Width="245" Height="130"/> </StackPanel> </StackPanel> </Window>

 

依赖属性的回调函数

PropertyChangedCallback是一个委托,表示在依赖属性的有效属性值更改时调用的回调。也就是说,当我们修改了某个依赖属性的值后,还希望立即做一些事情,那就在注册(定义)一个依赖属性时,将一个方法名通过PropertyMetadata构造函数注入,一并注册到依赖属性系统当中。

什么是PropertyMetadata:我们在定义一个依赖属性时,希望指明这个依赖属性的默认值,或者指明它的回调函数,这些信息都可以放到PropertyMetadata类中。

public class PropertyMetadata
{
      public PropertyMetadata();
      public PropertyMetadata(object defaultValue);
      public PropertyMetadata(PropertyChangedCallback propertyChangedCallback);
      public PropertyMetadata(object defaultValue, PropertyChangedCallback propertyChangedCallback);
      public PropertyMetadata(object defaultValue, PropertyChangedCallback propertyChangedCallback, CoerceValueCallback coerceValueCallback);
 
      public object DefaultValue { get; set; }
      public PropertyChangedCallback PropertyChangedCallback { get; set; }
      public CoerceValueCallback CoerceValueCallback { get; set; }
      protected bool IsSealed { get; }
 
      protected virtual void Merge(PropertyMetadata baseMetadata, DependencyProperty dp);
      protected virtual void OnApply(DependencyProperty dp, Type targetType);
}

DefaultValue 属性:表示依赖属性的默认值。

PropertyChangedCallback 属性:一个回调委托对象。当依赖属性值发现改变时触发执行。

CoerceValueCallback 属性:一个回调委托对象。表示强制转换时执行的业务逻辑,它会先于PropertyChangedCallback 委托触发。

举例说明:

/// <summary>
/// 格子数量
/// </summary>
public int Count
{
    get { return (int)GetValue(CountProperty); }
    set { SetValue(CountProperty, value); }
}
 
public static readonly DependencyProperty CountProperty =
    DependencyProperty.Register("Count", typeof(int), typeof(TrayControl), 
        new PropertyMetadata(
            0,
            new PropertyChangedCallback(OnCountPropertyChangedCallback),
            new CoerceValueCallback(OnCountCoerceValueCallback)));

在上面的代码中,我们实例化了一个PropertyMetadata对象,并传入了3个参数,分别是0、PropertyChangedCallback和CoerceValueCallback。其中第一个参数0表示Count属性的默认值,当外界改变Count 值时,首先会触发OnCountCoerceValueCallback回调函数的执行,然后是OnCountPropertyChangedCallback回调函数的执行。

 

标签:依赖,PropertyMetadata,DependencyProperty,using,public,属性
From: https://www.cnblogs.com/MingQiu/p/17997391

相关文章

  • C#(9):字段,属性,索引器
    函数体中的变量是局部变量字段的修饰符属性是为了简略字段的set()get()方法而发明的,可以起到同样的避免直接使用字段赋值来暴露数据的问题将属性封装成(refactory)属性方法的方法:ctrl+re只读方法只有get(){}而没有set(){}注意:只读方法属性和privateset(){}的区别,只读属性......
  • 在内网(不通公网)的情况下,使用pip安装python依赖包
    1.施工服务器后端部署一:准备一台环境与内网虚拟机相同的可以连接外网的虚拟机,python版本,操作系统版本保持一致二:在可以链接外网的机器上使用pip将依赖包的whl文件(也有可能是tar.gz或者tar格式,不过不影响后续使用)#将依赖下载到本地的某个文件中root@iZ8v2rbZ:/sdwork/pyyl#......
  • Maven学习之路--依赖范围scope 对于该包的依赖范围作用域,取值有:test、compile、provid
    Maven学习之路--依赖范围scope对于该包的依赖范围作用域,取值有:test、compile、provided、runtime。scope默认取值为compile。\   <scope></scope>表示对于该包的依赖范围作用域,取值有:test、compile、provided、runtime。scope默认取值为compile。<scope>test</scope>。te......
  • 未能加载文件或程序集或它的某一个依赖项
    https://jingyan.baidu.com/article/9f63fb916591e7c8400f0efe.html1、输入win+r,在弹出的输入框中输入inetmgr,快速打开iis2、点击应用程序池,可查看所有程序池 3、选中本项目使用的程序池,右键选择高级设置 4、看到启用32位应用程序位置为false 5、修改为true,保存。 ......
  • 无涯教程-Swift - Properties(属性)
    Swift4语言为类,枚举或结构提供属性以关联值。存储属性Swift4引入了存储属性的概念来存储常量和变量的,常量的存储属性由'let'关键字定义,变量的存储属性由'var'关键字定义。在定义期间,存储的属性提供"默认值"初始化期间,用户可以初始化和修改初始值structNumber{var......
  • “类图”之旅UML继承(泛化),实现,关联依赖,组合聚合.
     开启“类图”之旅说到对系统代码中的模型梳理,其实最好的方式还是使用UML类图。上个章节中老猫没有展开和大家分享UML类图,一个是由于篇幅的原因,第二个是老猫觉得类图对于咱们后续阅读框架源码以及底层设计模式有着相当大的帮助,所以很有必要将其作为单独一篇来和大家分享。如......
  • maven依赖的优先级
    优先级在Maven中,依赖的解析顺序和优先级如下:直接依赖优先级高于传递性依赖:如果你在项目的pom.xml中直接声明了某个依赖项,那么Maven会首先尝试使用这个直接声明的依赖项,而不考虑传递性依赖。直接依赖项的版本号会优先于传递性依赖。最近者优先:如果有多个传递性依赖解析......
  • 使用event.key属性来监听键盘按键
    在现代浏览器中,使用event.key属性来监听键盘按键是更好的做法。这是因为event.keyCode已经被弃用,并且在不同的浏览器和设备上可能会有不同的行为。根据MDN文档:event.keyCode:这个属性已经废弃,不推荐使用,因为它对于同一字符在不同布局下可能返回不同的值。event.key:这是推荐......
  • `pandas.ExcelFile.book` 是一个属性,它存储了底层 Excel 读取库(如 xlrd 或 openpyxl)创
    有点像excelwriter?`pandas.ExcelFile.book`是一个属性,它存储了底层Excel读取库(如xlrd或openpyxl)创建的workbook对象¹。你可以通过这个属性来访问Excel文件的更多详细信息,比如工作表的名字、单元格的格式等等。下面是一个如何使用`pandas.ExcelFile.book`的例子³:``......
  • React中的Key属性的作用
    在React中,我们常常会遇到需要渲染列表或循环生成组件的场景。为了提高性能和优化用户体验,React引入了一个特殊的属性——key。本文将详细介绍React中key属性的作用、原理,并提供一些最佳实践。一、Key属性的作用Key属性是React要求使用者在渲染多个组件时提供的一个特殊属性。它的作......