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

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

 

下面是依赖对像类的实现:(注,这里涉及到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方法至关重要,大家仔细看.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值