• WPF 依赖属性前言


    WPF 依赖属性前言

    ​ 在.net中,我们可以属性来获取或设置字段的值,不需要在编写额外的get和set方法,但这有一个前提,那就是需要在对象中拥有一个字段,才能在此字段的基础上获取或设置字段的值,比如:

    // 字段
    private int _age = 23;
    
    // 属性
    public int Age
    {
    	get { return _age; }
      	set { _age = value; }
    }
    

    ​ 但在wpf中,微软引入了一种依赖属性的方法来获取或者设置控件的值,之所以叫做依赖,是因为这个值能够从其他数据源中获取值(依赖在别人身上)而在控件对象上不保存这个值,所以依赖属性能有效地较少内存开销。

    依赖属性先简单说到这里,有时我们往往只是记住了结论而忽略了推导过程,下面的我对推到过程的一些思考:

    **如果让我们自己来实现依赖属性,我们该怎么实现呢? **

    首先能确定的是,我们不能定义控件拥有的所有字段,如果定义了,那么控件对象被创建时,所有字段都会被初始化,都会有一个初始值,内存就会被浪费,这样不行,所以我就想到了用字典Dictionary<int,object>的方式来存储需要的值。用int来存储一个索引值,保证控件中的每个属性索引值都不一样;用object来存储属性的值。

    由于每种控件都需要此字典来存储值,所以控件要有一个基类,这里我取名MyDependencyObject,它的结构大致应该是这样:

    	/// <summary>
        /// 依赖对象
        /// </summary>
        public class MyDependencyObject
        {
            private Dictionary<int, object> _keyValue = new Dictionary<int, object>();
    
            public object GetValue(int index)
            {
                return _keyValue[index];
            }
    
            public void SetValue(int index, object value)
            {
                _keyValue[index] = value;
            }
        }
    

    然后我们应该确定的是,控件的每个属性都应该有个索引值啊,这样才能在_keyValue字典中找到对应的值。然后又想到这个索引值应该是和对象无关的,是和类相关的,所以应该是一种静态的方式来得到这个索引值,所以实现的方式大概是这样:

    	/// <summary>
        /// 依赖对象
        /// </summary>
        public class MyDependencyObject
        {
            private Dictionary<int, object> _keyValue = new Dictionary<int, object>();
    
            public object GetValue(int index)
            {
                return _keyValue[index];
            }
    
            public void SetValue(int index, object value)
            {
                _keyValue[index] = value;
            }
    
            // 某种算法根据属性名获取一个索引值
            protected static int GetIndex(string propertyName)
            {
                // ...
            }
    
        }
    
    	/// <summary>
        /// 按钮控件
        /// </summary>
        public class MyButton : MyDependencyObject
        {
    
            /// <summary>
            /// 按钮内容属性索引
            /// </summary>
            public int ContentProperty = MyDependencyObject.GetIndex("Content");
    
            /// <summary>
            /// 按钮内容属性 
            /// </summary>
            public string Content
            {
                get
                {
                    return (string)base.GetValue(ContentProperty);
                }
                set
                {
                    base.SetValue(ContentProperty, value);
                }
            }
        }
    

    在基类MyDependencyObject中定义一个静态方法GetIndex,子类根据属性的名字来获取唯一的索引,然后在根据索引值来获取和设置值。

    虽然上面的方式已经可以达到节省内存的目的,但是对于属性更多的控制,比如属性的默认值、值变化回调、是否是只读的等等这些都达不到要求,所以属性应该被定义成更复杂的类型以完成更多的功能,而不是是用一个int的索引就完成了,所以我把这个更复杂的类型定义为MyDependencyProperty,它的结构如下:

    	/// <summary>
        /// 依赖属性
        /// </summary>
        public class MyDependencyProperty
        {
            /// <summary>
            /// 索引和属性数据字典
            /// </summary>
            public static Dictionary<int, MyDependencyProperty> _indexProperty = new Dictionary<int, MyDependencyProperty>();
    
            /// <summary>
            /// 注册依赖属性
            /// </summary>
            /// <param name="propertyName">属性名</param>
            /// <param name="ownType">拥有者类型</param>
            /// <param name="propertyData">属性数据</param>
            /// <returns></returns>
            public static MyDependencyProperty Register(string propertyName, Type ownType, MyPropertyMetadata propertyData)
            {
                MyDependencyProperty property = new MyDependencyProperty(propertyName, ownType, propertyData);
                int index = GetIndex(propertyName, ownType);
                _indexProperty[index] = property;
                return property;
            }
    
            /// <summary>
            /// 构造函数
            /// </summary>
            public MyDependencyProperty(string propertyName, Type ownType, MyPropertyMetadata data)
            {
                Name = propertyName;
                OwnType = ownType;
                DefaultMetaData = data;
            }
    
            /// <summary>
            ///属性名 
            /// </summary>
            public string Name { get; private set; }
    
            /// <summary>
            /// 拥有者类型
            /// </summary>
            public Type OwnType { get; private set; }
    
            /// <summary>
            /// 属性数据
            /// </summary>
            public MyPropertyMetadata DefaultMetaData { get; private set; }
    
            /// <summary>
            /// 使用某种算法按照属性名获取一个索引值 
            /// </summary>
            /// <param name="propertyName"></param>
            /// <param name="ownType"></param>
            /// <returns></returns>
            private static int GetIndex(string propertyName, Type ownType)
            {
                // ...
                return 0;
            }
        }
    
        /// <summary>
        /// 属性数据
        /// </summary>
        public class MyPropertyMetadata
        {
            public MyPropertyMetadata(object defaultValue)
            {
                DefaultValue = defaultValue;
            }
    
            /// <summary>
            /// 默认值
            /// </summary>
            public object DefaultValue { get; set; }
    
            // 其他的值 ...
        }
    

    MyDependencyProperty中包含了静态字段Dictionary<int, MyDependencyProperty> _indexProperty,它保存了索引-依赖属性的集合,整个应用程序只包含一个集合。通过静态方法GetIndex(string propertyName, Type ownType)计算索引值,计算索引值需要属性名和拥有者类型,这样可以计算出唯一的索引。

    像下面这样使用上面的依赖属性:

    	/// <summary>
        /// 依赖对象
        /// </summary>
        public class MyDependencyObject
        {
            private Dictionary<int, object> _keyValue = new Dictionary<int, object>();
    
            public object GetValue(MyDependencyProperty dp)
            {
                int index = MyDependencyProperty.GetIndex(dp.Name, dp.OwnType);
                // 如果字典的Key中不包含索引 那么返回属性的默认值
                if(!_keyValue.ContainsKey(index))
                {
                    return dp.DefaultMetaData.DefaultValue;
                }
                else
                {
                    return _keyValue[index];
                }
            }
    
            public void SetValue(MyDependencyProperty dp, object value)
            {
                int index = MyDependencyProperty.GetIndex(dp.Name, dp.OwnType);
                _keyValue[index] = value;
            }
    
        }
    
    
        /// <summary>
        /// 按钮控件
        /// </summary>
        public class MyButton : MyDependencyObject
        {
    
            /// <summary>
            /// 按钮内容依赖属性
            /// </summary>
            public MyDependencyProperty ContentProperty 
                = MyDependencyProperty.Register("Content", typeof(MyButton), new MyPropertyMetadata("Hello world"));
    
            /// <summary>
            /// 按钮内容属性 
            /// </summary>
            public string Content
            {
                get
                {
                    return (string)base.GetValue(ContentProperty);
                }
                set
                {
                    base.SetValue(ContentProperty, value);
                }
            }
        }
    

    简单的实现方式想到的也就这么多了,感觉算是有了依赖属性的雏形啦,WPF中的依赖属性下一篇在说吧。

  • 相关阅读:
    javascript中的几点说明
    repeater 绑定数组
    SQL语句之按in排序
    固定VS2005端口号
    关于asp.net中动态获取LinkButton的Text值的问题
    ASP.NET 2.0中直接得到本页面生成的HTML代码
    实现数据库的备份与还原的功能
    使用ASP.NET 2.0提供的WebResource管理内嵌资源(c#)
    [IIS]由于无法创建应用程序域,因此未能执行请求解决方案汇总
    VS2005中引用Microsoft Office COM组件
  • 原文地址:https://www.cnblogs.com/niecx/p/6854418.html
Copyright © 2020-2023  润新知