模拟实现WPF的依赖属性及绑定通知机制(3)--依赖对象

来源:互联网 发布:c语言bzero 编辑:程序博客网 时间:2024/04/30 11:02

下面是依赖对像类的实现:(注,这里涉及到INotifyPropertyChanged接口,大家可以参考MSDN文档了解).

 /// <summary>
    /// 依赖对像,主要提供属性值和属性绑定的管理。
    /// </summary>
    public class MyDependencyObject
    {
        private IDictionary<MyDependencyProperty, object> _dict = new Dictionary<MyDependencyProperty, object>();
        private IDictionary<MyDependencyProperty, MyBinding> _bindings = new Dictionary<MyDependencyProperty, MyBinding>();
        public void SetValue(MyDependencyProperty p, object val)
        {
           
            //如果设置值是默认值,则可不保存,以节省空间.
            object theOldValue = null;
            if (_dict.ContainsKey(p))
            {
                theOldValue = _dict[p];
                //如果已有设置值,且等于当前设置值,则退出
                if (theOldValue == val)
                {
                    return;
                }
                //如果设置值等于默认值,则删除已有的设置值。
                if (p.DefaultValue == val)
                {
                    _dict.Remove(p);
                    return;
                }
                //设置新的字典值
                _dict[p] = val;
            }
            else
            {
                //如果设置值不等于默认值,则增加设置值,否则不做任何设置。
                if (p.DefaultValue != val)
                {
                    _dict.Add(p, val);
                }
                else
                {
                    return;
                }
               
            }
           
            if (p.PropertyMetadata != null && p.PropertyMetadata.PropertyChangedCallback != null)
            {
                MyDependencyPropertyChangedEventArgs theArgs =
                    new MyDependencyPropertyChangedEventArgs(val, theOldValue, p);
                p.PropertyMetadata.PropertyChangedCallback(this, theArgs);
            }
            //如果是双向绑定,则需要同步数据到绑定数据源,这里假设需要双向绑定.
            if (_bindings.ContainsKey(p) == true)
            {
                MyBinding theBinding = _bindings[p];
                if (theBinding.TargetObject != null && theBinding.PropertyName != "")
                {
                    System.Reflection.PropertyInfo thePI = theBinding.TargetObject.GetType().GetProperty(theBinding.PropertyName);
                    if (thePI != null && thePI.CanWrite==true)
                    {
                        //对于有索引的设置值比较复杂一点,可利用反射来进行,这里只是演示简单属性。
                        //注意,如果目标类实现了INotifyPropertyChanged接口,并有修改触发机制,那么这里的设置
                        //会触发目标属性改变事件,就会触发MyDependencyObject_PropertyChanged执行,
                        //而MyDependencyObject_PropertyChanged里又调用了SetValue函数,这就会死循环,这也是
                        //为什么前面的代码中为什么要判断如果已经有的设置值等于当前设置新值直接退出的缘故,就是
                        //为了阻止死循环.当然,在目标属性中set里面做判断也可以,但这里一定要做,
                        //原因大家可以自己想一下。
                        thePI.SetValue(theBinding.TargetObject, val, null);
                    }
                }
            }
        }

        public object GetValue(MyDependencyProperty p)
        {
            //如果被动画控制,返回动画计算值。(可能会用到p.Name)

            //如果有本地值,返回本地值
            if (_dict.ContainsKey(p))
            {
                return _dict[p];
            }

            //如果有Style,则返回Style的值

            //返回从可视化树中继承的值

            //最后, 返回依赖属性的DefaultValue
            return p.DefaultValue;
        }
        /// <summary>
        /// 设置绑定属性,一样是模拟微软的干活,只不过微软的这个方法不是在依赖对象里实现的,
        /// 而是在UIElement里实现的.
        /// </summary>
        /// <param name="p"></param>
        /// <param name="Binding"></param>
        public void SetBinding(MyDependencyProperty p, MyBinding Binding)
        {
            MyBinding theOld = null;
            //需要先将老的绑定找到并记录,因为需要解除挂接.
            if (_bindings.ContainsKey(p))
            {
                theOld = _bindings[p];
                _bindings[p] = Binding;
            }
            else
            {
                _bindings.Add(p, Binding);
            }
            //删除旧的绑定.
            if (theOld != null)
            {
                if (theOld.TargetObject is INotifyPropertyChanged)
                {
                    ((INotifyPropertyChanged)theOld.TargetObject).PropertyChanged -= new PropertyChangedEventHandler(MyDependencyObject_PropertyChanged);
                }
            }

            //如果是单向绑定或者双向绑定则需要以下挂接。如果只是Onetime则不必要.
            if (Binding.TargetObject is INotifyPropertyChanged)
            {
                ((INotifyPropertyChanged)Binding.TargetObject).PropertyChanged += new PropertyChangedEventHandler(MyDependencyObject_PropertyChanged);
            }

        }
        /// <summary>
        /// 目标属性发生变化时的处理事件方法。
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void MyDependencyObject_PropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            MyDependencyProperty p = null;
            //找到绑定属性所在的依赖属性
            foreach (var b in _bindings)
            {
                if (b.Value.PropertyName == e.PropertyName)
                {
                    p = b.Key;
                    break;
                }
            }
            //不为空则处理.
            if (p != null)
            {
                System.Reflection.PropertyInfo thePI = sender.GetType().GetProperty(e.PropertyName);
                if (thePI != null && thePI.CanRead == true)
                {
                    object theVal = thePI.GetValue(sender, null);
                    SetValue(p, theVal);
                    //如果目标类INotifyPropertyChanged,绑定模式是ontime,则下面的代码就是要接触与目标属性的挂接.
                    if (sender is INotifyPropertyChanged)
                    {
                        ((INotifyPropertyChanged)sender).PropertyChanged += new PropertyChangedEventHandler(MyDependencyObject_PropertyChanged);
                    }
                }
            }
        }
    }

 

为了实现依赖属性和绑定属性之间的连动,SetBinding方法至关重要,大家仔细看.

原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 小孩性格好翘怎么办 一岁小宝宝打人怎么办 小孩子老喜欢打人怎么办 孩子被大人欺负怎么办 小孩识字量少怎么办 父母打架孩子该怎么办 小孩嘴唇里面烂怎么办 幼儿园小朋友很调皮怎么办 幼儿园小朋友上课调皮怎么办 孩子误冲游戏怎么办 遇到别的熊孩子怎么办 幼儿园遇到熊孩子怎么办 高铁上遇到熊孩子怎么办 幼儿爱打人家长怎么办 妈妈爱打孩子怎么办 35儿童爱打人怎么办? 一岁半宝宝太调皮怎么办 儿子高一不听话怎么办 小孩说了不听话怎么办 我的妈妈文盲怎么办 电脑键盘反拼音怎么办 小孩学习态度差怎么办 孩子不好好上学怎么办 小孩读书态度不好怎么办 幼儿园孩子不认识数字怎么办 一年级孩子拼音很差怎么办 孩子的拼音不好怎么办 小孩不会拼拼音怎么办 小孩拼音学不会怎么办 儿童l发音不准怎么办 小孩发音不标准怎么办 拼音l发音不准怎么办 孩子拼音声调分不清怎么办 小孩gk读成dt怎么办 拼音音调学不会怎么办 会拼音不会打字怎么办 大人拼音学不会怎么办 志愿服务经历少怎么办 医保报销发票丢失怎么办 费用发票丢失了怎么办 小孩乱拿东西怎么办