翼度科技»论坛 编程开发 .net 查看内容

8.依赖属性

9

主题

9

帖子

27

积分

新手上路

Rank: 1

积分
27
WPF的依赖属性系统,它是指WPF提供的一组服务,专门用来扩展WPF的属性功能,而受到这些服务支持的属性就称为依赖属性。
WPF的依赖属性系统对于开发者而言,几乎是感知不到的,它通过DependencyProperty类型的一些静态方法成员,提供一系列注册依赖属性或附加属性的功能,让我们可以向依赖属性系统注册属于我们自己写的依赖属性。
为了对比CLR普通属性与WPF的依赖属性的区别,直观的认知两者的概念,我们先来看看普通属性的定义
普通属性
  1. private int length = 0;
  2. public int Length
  3. {
  4.     get { return length; }
  5.     set { length = value; }
  6. }
复制代码
CLR普通属性的本质是在内部定义了一个私有字段,然后通过属性包装器将内部私有定段公开出来,get表示读出私有字段的值,set表示写入值到私有字段。假如WPF控件上的某个属性就是这类的普通属性,那么我们要更新这个属性的值,就只能赋值,用不能采用WPF的绑定方式了,因为只有依赖属性才支持绑定。
依赖属性的定义
在.cs里,输入propdp,然后按下tab键,VS会自动帮我们输入以下代码:
  1. public int MyProperty
  2. {
  3.     get { return (int)GetValue(MyPropertyProperty); }
  4.     set { SetValue(MyPropertyProperty, value); }
  5. }
  6. // Using a DependencyProperty as the backing store for MyProperty.
  7. // This enables animation, styling, binding, etc...
  8. public static readonly DependencyProperty MyPropertyProperty =
  9.     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
  1. <UserControl x:Class="HelloWorld.Controls.Widget"
  2.              xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  3.              xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  4.              xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
  5.              xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
  6.              xmlns:local="clr-namespace:HelloWorld.Controls"
  7.              mc:Ignorable="d"
  8.              x:Name="userControl"
  9.              FontSize="30"
  10.              Foreground="#666666"
  11.              BorderBrush="#8CDDCD"
  12.              d:DesignHeight="450"
  13.              d:DesignWidth="800">
  14.     <Border BorderBrush="{Binding ElementName=userControl,Path=BorderBrush}">
  15.         <Border.Style>
  16.             
  17.         </Border.Style>
  18.         <Grid>
  19.             <Grid.ColumnDefinitions>
  20.                 <ColumnDefinition/>
  21.                 <ColumnDefinition Width="auto"/>
  22.             </Grid.ColumnDefinitions>
  23.             <Grid.RowDefinitions>
  24.                 <RowDefinition/>
  25.                 <RowDefinition/>
  26.             </Grid.RowDefinitions>
  27.             <TextBlock Grid.Row="0" Grid.Column="0" Text="{Binding Value}"
  28.                        Foreground="{Binding ElementName=userControl,Path=Foreground}"
  29.                        FontSize="{Binding ElementName=userControl,Path=FontSize}" />
  30.             <TextBlock Grid.Row="1" Grid.Column="0" Text="{Binding Title}"
  31.                        Foreground="{Binding ElementName=userControl,Path=Foreground}"
  32.                        FontSize="14" TextWrapping="Wrap"/>
  33.             <TextBlock Grid.Row="0" Grid.Column="1" Text="{Binding Icon}"
  34.                        Foreground="{Binding ElementName=userControl,Path=BorderBrush}"
  35.                        FontSize="26" Grid.RowSpan="2" VerticalAlignment="Center"/>
  36.         </Grid>   
  37.     </Border>
  38. </UserControl>
复制代码
在这里,我们一共实例化了5个控件,最外层的Border控件用来做修饰,且它的边框颜色绑定了当前UserControl控件的边框颜色。Grid里面有3个TextBlock文字块控件,其中的前景色、字号也分别绑定了当前UserControl的属性。这样做的好处是,将来实例化这个Widget自定义用户控件时,我们就可以设置它的相关属性,从而改变内部的边框颜色、字体颜色和字体大小。
需要注意的是,3个TextBlock控件Text属性分别绑定了Value、Title、Icon三个属性,这三个属性就是我们要去自定义的依赖属性。
定义依赖属性
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5. using System.Threading.Tasks;
  6. using System.Windows;
  7. using System.Windows.Controls;
  8. using System.Windows.Data;
  9. using System.Windows.Documents;
  10. using System.Windows.Input;
  11. using System.Windows.Media;
  12. using System.Windows.Media.Imaging;
  13. using System.Windows.Navigation;
  14. using System.Windows.Shapes;
  15. namespace HelloWorld.Controls
  16. {
  17.     /// <summary>
  18.     /// Widget.xaml 的交互逻辑
  19.     /// </summary>
  20.     public partial class Widget : UserControl
  21.     {  
  22.         public Widget()
  23.         {
  24.             InitializeComponent();
  25.             DataContext = this;
  26.         }
  27.         public string Icon
  28.         {
  29.             get { return (string)GetValue(IconProperty); }
  30.             set { SetValue(IconProperty, value); }
  31.         }
  32.         public static readonly DependencyProperty IconProperty =
  33.             DependencyProperty.Register("Icon", typeof(string), typeof(Widget), new PropertyMetadata("☻"));
  34.         public string Title
  35.         {
  36.             get { return (string)GetValue(TitleProperty); }
  37.             set { SetValue(TitleProperty, value); }
  38.         }
  39.         public static readonly DependencyProperty TitleProperty =
  40.             DependencyProperty.Register("Title", typeof(string), typeof(Widget), new PropertyMetadata("请输入标题"));
  41.         public string Value
  42.         {
  43.             get { return (string)GetValue(ValueProperty); }
  44.             set { SetValue(ValueProperty, value); }
  45.         }
  46.         public static readonly DependencyProperty ValueProperty =
  47.             DependencyProperty.Register("Value", typeof(string), typeof(Widget), new PropertyMetadata("内容"));
  48.     }
  49. }
复制代码
通过在C#后端输入propdp,再按下tab键,VS会自动创建依赖属性的定义模板代码,我们只需要修改模板中的属性类型、属性名、和注册依赖属性时的相关参数即可。如上述代码所示,这里分别注册了IconProperty、TitleProperty和ValueProperty三个依赖属性,并且将它们注册到Widget类型上。由于依赖属性天生具有属性通知功能,所以我们不必去实现INotifyPropertyChanged接口,只需要将当前类做为ViewModel传给Widget的DataContent,前端的控件就可以绑定Value、Title、Icon三个属性了。
使用自定义控件
  1. <Window x:Class="HelloWorld.MainWindow"
  2.         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  3.         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  4.         xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
  5.         xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
  6.         xmlns:local="clr-namespace:HelloWorld" <br>        <strong>xmlns:controls="clr-namespace:HelloWorld.Controls"</strong>
  7.         mc:Ignorable="d" Background="LightGray"
  8.         Title="WPF中文网 - www.wpfsoft.com" Height="350" Width="500">
  9.     <StackPanel>
  10.         <StackPanel Orientation="Horizontal">
  11.             <<strong>controls</strong>:<strong>Widget</strong> Icon="¥"
  12.                              Title="本年度销售总额"
  13.                              Value="38452.21"
  14.                              Width="215"
  15.                              Height="100"/>
  16.             <controls:Widget Icon="◈"
  17.                              Title="系统访问量"
  18.                              Value="9985"
  19.                              Foreground="#415767"
  20.                              BorderBrush="#87BEE4"
  21.                              Width="225"
  22.                              Height="110"/>
  23.         </StackPanel>
  24.         <StackPanel Orientation="Horizontal">
  25.             <controls:Widget Icon="◈"
  26.                              Title="系统访问量"
  27.                              Value="9985"
  28.                              Foreground="#495E26"
  29.                              BorderBrush="#C1E487"
  30.                              Width="235"
  31.                              Height="120"/>
  32.             <controls:Widget Icon="㍿"
  33.                              Title="日本丰田汽车国际进出口贸易有限公司"
  34.                              Value="股票代码95568"
  35.                              Foreground="#4E3A55"
  36.                              BorderBrush="#CB87E4"
  37.                              FontSize="24"
  38.                              Width="245"
  39.                              Height="130"/>
  40.         </StackPanel>
  41.     </StackPanel>
  42.    
  43. </Window>
复制代码
 
依赖属性的回调函数
PropertyChangedCallback是一个委托,表示在依赖属性的有效属性值更改时调用的回调。也就是说,当我们修改了某个依赖属性的值后,还希望立即做一些事情,那就在注册(定义)一个依赖属性时,将一个方法名通过PropertyMetadata构造函数注入,一并注册到依赖属性系统当中。
什么是PropertyMetadata:我们在定义一个依赖属性时,希望指明这个依赖属性的默认值,或者指明它的回调函数,这些信息都可以放到PropertyMetadata类中。
  1. public class PropertyMetadata
  2. {
  3.       public PropertyMetadata();
  4.       public PropertyMetadata(object defaultValue);
  5.       public PropertyMetadata(PropertyChangedCallback propertyChangedCallback);
  6.       public PropertyMetadata(object defaultValue, PropertyChangedCallback propertyChangedCallback);
  7.       public PropertyMetadata(object defaultValue, PropertyChangedCallback propertyChangedCallback, CoerceValueCallback coerceValueCallback);
  8.       public object DefaultValue { get; set; }
  9.       public PropertyChangedCallback PropertyChangedCallback { get; set; }
  10.       public CoerceValueCallback CoerceValueCallback { get; set; }
  11.       protected bool IsSealed { get; }
  12.       protected virtual void Merge(PropertyMetadata baseMetadata, DependencyProperty dp);
  13.       protected virtual void OnApply(DependencyProperty dp, Type targetType);
  14. }
复制代码
DefaultValue 属性:表示依赖属性的默认值。
PropertyChangedCallback 属性:一个回调委托对象。当依赖属性值发现改变时触发执行。
CoerceValueCallback 属性:一个回调委托对象。表示强制转换时执行的业务逻辑,它会先于PropertyChangedCallback 委托触发。
举例说明:
  1. /// <summary>
  2. /// 格子数量
  3. /// </summary>
  4. public int Count
  5. {
  6.     get { return (int)GetValue(CountProperty); }
  7.     set { SetValue(CountProperty, value); }
  8. }
  9. public static readonly DependencyProperty CountProperty =
  10.     DependencyProperty.Register("Count", typeof(int), typeof(TrayControl),
  11.         new PropertyMetadata(
  12.             0,
  13.             new PropertyChangedCallback(OnCountPropertyChangedCallback),
  14.             new CoerceValueCallback(OnCountCoerceValueCallback)));
复制代码
在上面的代码中,我们实例化了一个PropertyMetadata对象,并传入了3个参数,分别是0、PropertyChangedCallback和CoerceValueCallback。其中第一个参数0表示Count属性的默认值,当外界改变Count 值时,首先会触发OnCountCoerceValueCallback回调函数的执行,然后是OnCountPropertyChangedCallback回调函数的执行。
 

来源:https://www.cnblogs.com/MingQiu/p/17997391
免责声明:由于采集信息均来自互联网,如果侵犯了您的权益,请联系我们【E-Mail:cb@itdo.tech】 我们会及时删除侵权内容,谢谢合作!

举报 回复 使用道具