zoukankan      html  css  js  c++  java
  • WPF依赖属性(续)(3)依赖属性存储

           在之前的两篇,很多朋友参与了讨论,也说明各位对WPF/SL计数的热情,对DP系统各抒已见,当然也出现了一些分歧. 以下简称DP为依赖属性

    总结下上文:

    • 讨论了DP的内存问题
    • 讨论了依赖属性与附加属性的区别

    下面我们继续讨论DP的存储.

    存储依赖属性

    (1)确保DP的唯一性
    所有的DP由一个内部静态的哈希表(PropertyFromName)维护,一个对象定义的DP属性键值不可以重复,相同键值的DP可以定义在其他对象中,为确保属性唯一性,使用DP属性键值和对象的HashCode组成,内部定义了一个FromNameKey对象,如下

    private class FromNameKey
    {
        // Fields
        private int _hashCode;
        private string _name;
        private Type _ownerType;
    
        // Methods
        public FromNameKey(string name, Type ownerType)
        {
            this._name = name;
            this._ownerType = ownerType;
            this._hashCode = this._name.GetHashCode() ^ this._ownerType.GetHashCode();
        }
    
        public override int GetHashCode()
        {
            return this._hashCode;
        }
    }

    以下为内部精简代码,忽略其他部分

    public static DependencyProperty Register(string name, Type propertyType, Type ownerType, PropertyMetadata defaultMetadata, ValidateValueCallback validateValueCallback)
    {
        FromNameKey key = new FromNameKey(name, ownerType);
        if (PropertyFromName.Contains(key))
        {
            throw new ArgumentException(SR.Get("PropertyAlreadyRegistered", new object[] { name, ownerType.Name }));
        }
        DependencyProperty dp = new DependencyProperty(name, propertyType, ownerType, defaultMetadata, validateValueCallback);
        PropertyFromName[key] = dp;
        return dp;
    }

    (2)何时注册DP
    由于DP是以静态方法注册的,所以当拥有此DP的对象初始化后则会注册该对象DP,我们来看下,当一个Window窗体拥有一些简单的元素初始化后,其DP的数量
    image

    依赖属性赋值与取值

    在赋值与取值之前必须了解下情况
    (1)DP不直接参与自身的存值与取值操作,而是由拥有DP的对象(依赖项对象,且称为DP对象)完成,该对象从DispatcherObject派生.使用GetValue和SetValue方法
    (2)DP对象对DP进行赋值前,必须先索引DP,用内部键值索引太麻烦,则为内部加了一个Index索引值,可以用DependencyProperty .GlobalIndex拿到这个值,DP列表数据结构内部维护着一个列表可以根据Index进行索引,这样便于DP对象查询DP

    维护本地依赖属性值

    什么是本地DP值,即是你修改过的DP属性的值,而非采用默认的DP属性元数据中的值
    在之前
    介绍过,每个DP都拥有的一个默认值,现在必须要把DP与拥有该DP的对象联系起来.
    DP对象内部维护着一份本地DP值列表,当DP有所修改,那么该DP会被记录下来,保存到内部的一个列表中.如下代码
    var people = new DPCustomPeople();
    people.SetValue(DPCustomPeople.AgeProperty, 0);
    AgeProperty的值将会被保存起来.
    若要获取DP对象的本地DP值,DependencyObject公开了一个GetLocalValueEnumerator方法,可以获取该列表,
    也可以使用ReadLocalValue方法读取一个DP的本地值
    注意:GetValue方法如果本地值为空则返回默认值,但ReadLocalValue则会返回DependencyProperty.UnsetValue
    如下测试代码
    static void Main(string[] args)
    {
        var people = new DPCustomPeople();
        Console.WriteLine("Before SetValue");
        PrintLoaclValue(people);
        Console.WriteLine();
        people.SetValue(DPCustomPeople.AgeProperty, 0);
        Console.WriteLine("After SetValue");
        PrintLoaclValue(people);
           
    }
    
    static void PrintLoaclValue(DependencyObject obj)
    {
        var enumerator = obj.GetLocalValueEnumerator();
        while (enumerator.MoveNext())
        {
            Console.WriteLine("Property:" + enumerator.Current.Property + "/Value:" + enumerator.Current.Value);
        }
    }
    输出结果:
    image

    多属性值(属性百宝箱)

    WPF具有强大的绑定功能,当初学WPF时,往往会把对属性的赋值与绑定混淆了,对于这个绑定功能也是非常的陌生,是如何实现的,我们且不讨论这一议题.来看看维护这份多属性值的数据结构EffectiveValueEntry,测试以下代码

    people.SetValue(DPCustomPeople.AgeProperty, 25);
    people.SetValue(DPCustomPeople.NameProperty, "Clingingboy");
    我们将在DispatcherObject内部看到,一个变量名为_effectiveValues的数组
    image

    这就意味着对DP的修改要此_effectiveValues关联起来,那么DP的GlobaIndex就起到了作用
    注意:DP一旦创建,GlobaIndex就固定了,但每个DP对象的_effectiveValues则是动态创建的,所以在赋值与取值的时候,要将其关联起来,内部采用了LookupEntry方法,根据DP的索引值,去列表中找到索引,并返回索引结果

    EntryIndex entryIndex = this.LookupEntry(dp.GlobalIndex);

    struct EntryIndex
    {
        private uint _store;
        public EntryIndex(uint index);
        public EntryIndex(uint index, bool found);
        public bool Found { get; }
        public uint Index { get; }
    }

    同时其内部还具备多个操作EffectiveValueEntry的方法

    image

    这篇先到这里,这部分还未完

  • 相关阅读:
    面试题收集——Java基础部分(一)
    Eclipse在线安装SVN
    一、 Log4E插件下载
    MyEclipse使用总结——MyEclipse中配置WebLogic12c服务器
    MyEclipse使用总结——使用MyEclipse打包带源码的jar包
    MyEclipse使用总结——设置MyEclipse使用的Tomcat服务器
    MyEclipse使用总结——设置MyEclipse开发项目时使用的JDK
    MyEclipse使用总结——修改MyEclipse默认的Servlet和jsp代码模板
    MyEclipse使用总结——MyEclipse文件查找技巧
    CMSIS Example
  • 原文地址:https://www.cnblogs.com/Clingingboy/p/1757294.html
Copyright © 2011-2022 走看看