WPF 依赖属性,用户控件依赖属性(DependencyProperty 依赖属性、GetValue() SetValue() CLR属性包装器、SetBinding 数据绑定)

前端之家收集整理的这篇文章主要介绍了WPF 依赖属性,用户控件依赖属性(DependencyProperty 依赖属性、GetValue() SetValue() CLR属性包装器、SetBinding 数据绑定)前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。

前面讲了,数据目标="{Binding 数据源}",绑定的数据源,这里我们讲得数据目标


一、属性(Property)的来龙去脉。

1、变量、函数

数据(变量)+算法(函数)=程序


2、字段、方法

类的作用:把散落的程序中的变量函数进行归档封装,并控制它的访问。被封装在类里的变量称为字段(Field),被封装在类里的函数称为方法(Method)


我们使用private、public等修饰符来控制字段或方法的可访问性。使用static决定字段或方法是对类有意义,还是对类的实例有意义。

如:Age对类的实例有意义,Amount(总量)对类有意义。

c#语言规定:对类有意义的字段或方法使用static关键字修饰,称为静态成员,通过类名加访问操作符(即“.”操作符)可以访问它们。

对类的实例有意义的字段和方法不加static关键字,称为非静态成员或实例成员。


静态成员、非静态成员在内存中的结构?

静态字段在内存中只有一个拷贝,非静态字段则每个实例拥有一个拷贝。无论方法是否为静态的,内存只有一个拷贝。


3、属性是如何演变出来的?

CLR属性是对字段进行封装,控制字段的可访问性,有效性。不会增加内存负担,仅仅是个语法糖衣(Syntax Sugar)。

class Human
    {
        private int age;

        public int Age
        {
            //可访问性(只读)
            get { return age; }
            private set {
                                //有效性
                                if (value >= 0 && value <= 100)
                                {
                                    age = value;
                                }
                                else
                                {
                                    throw new OverflowException("Age不在范围");
                                }           
                        }
        }

    }



二、依赖属性(Dependency Property)

依赖属性就是一种可以自己没有值,并能通过使用Binding 从数据源获得值(依赖在别人身上)的属性。拥有依赖属性的对象被称为“依赖对象”。与传统的CLR属性和面向对象思想相比依赖属性有很多新颖之处,

其中包括
节省实例对内存的开销。

属性值可以通过Binding依赖在其他对象上。

具有内置的更改通知支持(当源对象中改变依赖项属性的值时,会立即更新目标对象中的绑定属性)。


1、依赖属性对内存的使用方式

传统.NET开发中,一个对象所占用的内存空间在调用new操作符进行实例化的时候就已经决定。

WPF允许对象在被创建的时候,并不包含字段所占用的空间,只保留需要用字段能够获得默认值、借用其它对象字段或实时分配空间的能力。这种对象称为依赖对象(Dependency Object),而它实时获取数据的能力则依靠依赖属性(Dependency Property)来实现。


DependencyObject 依赖对象是WPF系统中相当底层的一个基类。WPF所有UI控件都是依赖对象,节省内存开销。

DependencyObject 继承树如下图结构:


public class DependencyObject : System.Windows.Threading.DispatcherObject
    {
        //DependencyObject具有GetValue或SetValue方法,以DependencyProperty作为参数
        //GetValue通过DependencyProperty对象获取数据
        public object GetValue(DependencyProperty dp)
        { 
        
        }

        //SetValue通过DependencyProperty对象存储数据
        public void SetValue(DependencyProperty dp,object value)
        { 
        
        }
    }



实例一:依赖属性

定义依赖属性,必须以DependencyObject为宿主、借助它的SetValue和GetValue方法,写入或读取。

(1)、想定义依赖属性,类必须继承DependencyObject。

(2)、 变量由 public static readonly 三个修饰符修饰。

(3)、使用DependencyProperty.Register方法创建依赖属性的实例Name+Property,并对它进行注册


//定义依赖属性,类必须继承 DependencyObject
public class Student : DependencyObject
{
//定义依赖属性必须是 public static readonly DependencyProperty Name+Property为后缀
public static readonly DependencyProperty NameProperty =
DependencyProperty.Register("Name",typeof(string),typeof(Student));
//CLR属性包装器名称(属性名称)、依赖属性类型(属性类型)、依赖属性关联到哪个类型(宿主类型)

public static readonly DependencyProperty AgeProperty=

DependencyProperty.Register("Age",typeof(int),typeof(Student),new PropertyMetadata(20));

//new PropertyMetadata(20) 数据默认元素据20(属性默认值)

}


按钮调用

Student s = new Student();//创建Student实例
            s.SetValue(Student.NameProperty,"111");  //SetValue方法将111,存储进依赖属性
            TextBox1.SetValue(TextBox.TextProperty,s.GetValue(Student.NameProperty)); //GetValue 获取依赖属性的值


实例二:CLR属性包装器:以“实例属性”的形式向外界暴露依赖属性


为依赖属性 添加一个 CLR属性包装器,有个这个包装,就相当于为依赖对象准备了用于暴露数据的 Binding Path。也就是说,现在的依赖对象已经具备了扮演数据源和数据目标双重角色的能力。值得注意的是,尽管Student类没有实现INotifyPropertyChanged 接口,当属性的值发生变化时与之关联的Binding对象依然可以得到通知

public string Name
{
get { return (string)GetValue(NameProperty); }
set { SetValue(NameProperty,value); }
}


调用方法

Student s=new Student();

s.Name="111";

TextBox1.text=s.Name;


实例三:借用FrameWorkElement类的 SetBinding方法,使Student类有绑定方法(推荐)

// 摘要:
        //     创建 System.Windows.Data.BindingExpressionBase 的新实例,并将其与指定的绑定目标属性关联。
        //
        // 参数:
        //   target:
        //     绑定的绑定目标。
        //
        //   dp:
        //     绑定的目标属性。
        //
        //   binding:
        //     描述绑定的 System.Windows.Data.BindingBase 对象。
        //
        // 返回结果:
        //     为指定的属性创建并与之相关联的 System.Windows.Data.BindingExpressionBase 的实例。System.Windows.Data.BindingExpressionBase
        //     类是 System.Windows.Data.BindingExpression、System.Windows.Data.MultiBindingExpression
        //     和 System.Windows.Data.PriorityBindingExpression 的基类。
        //
        // 异常:
        //   System.ArgumentNullException:
        //     target 参数不能为 null。
        //
        //   System.ArgumentNullException:
        //     dp 参数不能为 null。
        //
        //   System.ArgumentNullException:
        //     binding 参数不能为 null。
        public static BindingExpressionBase SetBinding(DependencyObject target,DependencyProperty dp,BindingBase binding)

BindingOperations.SetBinding(this,dp,binding)

//数据源
    public class Student : DependencyObject
    {

        //小技巧:输入 propdp,按Tab键。可以快速建依赖属性

        
        //CLR属性包装器
        public string Name
        {
            get { return (string)GetValue(NameProperty); }
            set { SetValue(NameProperty,value); }
        }

        //依赖属性
        public static readonly DependencyProperty NameProperty =
            DependencyProperty.Register("Name",typeof(Student));


        //借用 SetBinding包装
        public BindingExpressionBase SetBinding(DependencyProperty dp,BindingBase binding)
        {
            return BindingOperations.SetBinding(this,binding);
        }

    }


Student stu;
        public Window1()
        {
            InitializeComponent();
            stu = new Student();
            //Student的Name属性 关联 textBox1的Text属性
            stu.SetBinding(Student.NameProperty,new Binding("Text") { Source=textBox1});
            //textBox2的Text属性 关联 Student的Name属性
            textBox2.SetBinding(TextBox.TextProperty,new Binding("Name") { Source = stu });           
        }


三、依赖属性存取的秘密

1、DependencyProperty.Register 源码分析

(1)、创建一个DependencyProperty 实例,并用CLR属性包装器名和宿主类型进行异或生成键(key),把DependencyProperty 实例存到Hashtable(PropertyFromName) 中

private static HashtablePropertyFromName=newHashtable();

DependencyProperty dp=newDependencyProperty(name,PropertyType,ownerType,defaultMetadata,validateValueCallback);

PropertyFromName[key]=dp;


(2)、最后返回DependencyProperty 实例

return dp;



2、DependencyObject的GetValue、SetValue

GetValue源码分析:

//dp.GlobalIndex 是:CLR属性包装器名和宿主类型进行异或生成键(key)

EntryIndex entryIndex=LookupEntry(dp.GlobalIndex);


//每个依赖对象实例都自带一个EffectiveValueEntry 类型数组(存取值),当某个依赖属性的值要被读取时,算法会从这个数组中检索值

//如果数组中没有包含这个值,算法会返回依赖属性的默认值(这个值由依赖属性的DefaultMetadata来提供的)

//static关键字所修饰的依赖属性对象,用来检索数据。

//为了保障GlobalIndex稳定性,使用readonly关键字修饰。

EffectiveValueEntry valueEntry=GetValueEntry(entryIndex,null,RequestFlags.FullyResolved)

return valueEntry.Value;


依赖属性读取是优先级控制的,由先到后依次是:


SetValue源码分析:

EffectiveValueEntry数组,修改值,存储值。


四、附加属性(Attached Properties)

如:

<Canvas Margin="10">
<TextBox Canvas.Top="0"/>
<TextBox Canvas.Top="30"/>
<TextBox Canvas.Top="60"/>
</Canvas>


实例:人在学校获得班级属性

DependencyProperty.RegisterAttached()

class School : DependencyObject
    {

        //输入    propa,按Tab键。继续按Tab键,可以在几个空缺间轮换并修改,直到按下Enter键。
    
        public static int GetGrade(DependencyObject obj)
        {
            return (int)obj.GetValue(GradeProperty);
        }

        public static void SetGrade(DependencyObject obj,int value)
        {
            obj.SetValue(GradeProperty,value);
        }

        //使用RegisterAttached方法注册附加属性(班级Grade)
        public static readonly DependencyProperty GradeProperty =
            DependencyProperty.RegisterAttached("Grade",typeof(School),new UIPropertyMetadata(0));

        
    }
    class Human : DependencyObject
    { 
    
    }


Human human = new Human();
            School.SetGrade(human,6);
            int grade= School.GetGrade(human);
            MessageBox.Show(grade.ToString());


六、用户控件 依赖属性

效果:动画数字从100到200


代码

1、用户控件 ShowNumberControl

<UserControl x:Class="WpfApplication1.ShowNumberControl"
             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" 
             mc:Ignorable="d" 
             d:DesignHeight="300" d:DesignWidth="300">
    <Grid>
        <Label x:Name="numberDisplay" Height="50" Width="200" Background="LightBlue"/>
    </Grid>
</UserControl>


创建依赖属性CurrentNumberProperty。

当依赖属性的值发生改变时,先验证值是否正确。

如果正确,就将依赖属性最新的值赋值给Label

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
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 WpfApplication1
{
    /// <summary>
    /// ShowNumberControl.xaml 的交互逻辑
    /// </summary>
    public partial class ShowNumberControl : UserControl
    {
        public ShowNumberControl()
        {
            InitializeComponent();
        }

        public int CurrentNumber
        {
            get { return (int)GetValue(CurrentNumberProperty); }
            set { SetValue(CurrentNumberProperty,value); }
        }

        //依赖属性
        public static readonly DependencyProperty CurrentNumberProperty =
            DependencyProperty.Register("CurrentNumber",typeof(ShowNumberControl),new UIPropertyMetadata(100,new PropertyChangedCallback(CurrentNumberChanged)),new ValidateValueCallback(ValidateCurrentNumber)
            );

        //验证依赖属性值
        public static bool ValidateCurrentNumber(object value)
        {
            if (Convert.ToInt32(value) >= 0 && Convert.ToInt32(value) <= 500)
                return true;
            else
                return false;
        }

        //当依赖属性更改后,将用户控件中的Label设置最新依赖属性的值。
        public static void CurrentNumberChanged(DependencyObject d,DependencyPropertyChangedEventArgs e)
        {
            ShowNumberControl c = (ShowNumberControl)d;
            Label theLabel = c.numberDisplay;
            theLabel.Content = e.NewValue.ToString();
        }

        
    }
}


new UIPropertyMetadata(100,new PropertyChangedCallback(CurrentNumberChanged))

依赖属性的默认值 100。

属性更改后,委托执行方法CurrentNumberChanged


2、窗体调用 用户控件

<Window x:Class="WpfApplication1.Window3"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:myControl="clr-namespace:WpfApplication1"
Title="Window3" Height="300" Width="300">
<Grid>
<myControl:ShowNumberControl x:Name="myShowNumberControl" CurrentNumber="100">
<myControl:ShowNumberControl.Triggers> Triggers触发器
<EventTrigger RoutedEvent="myControl:ShowNumberControl.Loaded"> EventTrigge事件触发器
<BeginStoryboard> BeginStoryboard 开始动画板
<Storyboard TargetProperty="CurrentNumber"> TargetProperty 动画板目标属性 <Int32Animation From="100" To="200" Duration="0:0:10"/> </Storyboard> </BeginStoryboard> </EventTrigger> </myControl:ShowNumberControl.Triggers> </myControl:ShowNumberControl>
</Grid> </Window>

猜你在找的设计模式相关文章