• Silverlight的依赖属性与附加属性(六)


    WPF依赖属性(续)(2)依赖属性与附加属性的区别

         接上篇,感谢各位的评论,都是认为依赖属性的设计并不是为了节省内存,从大的方面而讲是如此.样式,数据绑定,动画样样都离不开它.这篇我们来看下依赖属性与附加属性的区别.

    注册方法

    我们知道注册依赖属性使用Register方法,注册附加属性则使用RegisterAttached方法,如下代码

    public class DPCustomPeople:DependencyObject
    {   
        public static readonly DependencyProperty AgeProperty =
            DependencyProperty.Register("Age", typeof(int), typeof(DPCustomPeople));
    
        public static readonly DependencyProperty Age2Property =
            DependencyProperty.RegisterAttached("Age2", typeof(int), typeof(DPCustomPeople));
    }

    包装属性

    public int Age
    {
        get { return (int)GetValue(AgeProperty); }
        set { SetValue(AgeProperty, value); }
    }
    
    public int Age2
    {
        get { return (int)GetValue(Age2Property); }
        set { SetValue(Age2Property, value); }
    }

    一般默认依赖属性使用CLR属性进行包装,附加属性使用Get,Set方法进行包装. 
    下面我则均以属性进行包装,从表面上看两者除了方法不同,其他都是一样的 
    那么附加属性的魔力到底何在呢?

    XAML的魔力

    public class AttachEntity
    {
    
        public static double GetWidth(DependencyObject obj)
        {
            return (double)obj.GetValue(Button.WidthProperty);
        }
    
        public static void SetWidth(DependencyObject obj, double value)
        {
            obj.SetValue(Button.WidthProperty, value);
        }
    }
    考虑以上代码,你可能从来都没有试过在没有附加属性的情况下,下面的只有Get,Set的方法,然后尝试在XAML中设置 
    <Button local:AttachEntity.Width="300" Content="Button" />
    

    奇迹出现,赋值成功,所以我们一直以来都误解了附加属性. 
    image

    即来看到这里,我们不妨试试依赖属性 

    public static readonly DependencyProperty AgeProperty =
        DependencyProperty.Register("Age", typeof(int), typeof(DPCustomPeople),
        new UIPropertyMetadata(0,(sender,args)=>
                                     {
                                         var element = sender as DPCustomPeople;
                                     }));
    
    public static int GetAge(DependencyObject obj)
    {
        return (int)obj.GetValue(AgeProperty);
    }
    
    public static void SetAge(DependencyObject obj, int value)
    {
        obj.SetValue(AgeProperty, value);
    }

    <Button local:DPCustomPeople.Age="2" Content="Button" Name="button1"/>
    

    程序运行正常,发生下列情况

    • 可以取到附加属性值
    • 属性变更通知没有发生 

    默认属性元数据

    现在是时候来看看依赖属性与附加属性的区别了,以下是内部注册方法

    第一段:注册依赖属性的方法

    public static DependencyProperty Register(string name, Type propertyType, Type ownerType, PropertyMetadata typeMetadata, ValidateValueCallback validateValueCallback)
    {
        RegisterParameterValidation(name, propertyType, ownerType);
        PropertyMetadata defaultMetadata = null;
        if ((typeMetadata != null) && typeMetadata.DefaultValueWasSet())
        {
            defaultMetadata = new PropertyMetadata(typeMetadata.DefaultValue);
        }
        DependencyProperty property = RegisterCommon(name, propertyType, ownerType, defaultMetadata, validateValueCallback);
        if (typeMetadata != null)
        {
            property.OverrideMetadata(ownerType, typeMetadata);
        }
        return property;
    }

    第二段:注册附加属性方法

    public static DependencyProperty RegisterAttached(string name, Type propertyType, Type ownerType, PropertyMetadata defaultMetadata, ValidateValueCallback validateValueCallback)
    {
        RegisterParameterValidation(name, propertyType, ownerType);
        return RegisterCommon(name, propertyType, ownerType, defaultMetadata, validateValueCallback);
    }


    请先忽略上面划线部分的代码,现在我们看到在注册依赖属性时,前后多了一些处理.

    原来是属性元数据在作怪。

    注册依赖属性时,会传入一个属性元数据,但内部定义了一个默认的属性元数据(defaultMetadata ),当依赖属性注册完毕后,则重写了属性元数据(OverrideMetadata),而注册附加属性时,则直接传入参数.这个参数则直接作为了依赖属性的默认元数据,如下代码

    var people = new DPCustomPeople();
    var defaultMetadata=DPCustomPeople.AgeProperty.DefaultMetadata;
    var metadata = DPCustomPeople.AgeProperty.GetMetadata(people);


    附加属性只有默认属性元数据,根据以上源码,我们甚至可以改造附加属性为依赖属性,下面的附加属性则变成了依赖属性

    注意点: 
    (1)重写属性元数据是一个合并的过程,所以重写的变更事件并不会触发 
    (2)若属性元数据已经注册完毕,同个类型的属性元数据不可重复重写 
    (3)默认属性元数据无法更改

    public static readonly DependencyProperty Age3Property;
    static DPCustomPeople()
    {
        var metaData = new PropertyMetadata(0, new PropertyChangedCallback((sender, args) =>
        {
            
            MessageBox.Show("hello1");
        }));
        PropertyMetadata defaultMetadata = null;
        if ((metaData != null))
        {
            defaultMetadata = new PropertyMetadata(metaData.DefaultValue, new PropertyChangedCallback((sender, args) =>
            {
                MessageBox.Show("hello2");
            }));
        }
        Age3Property = DependencyProperty.RegisterAttached("Age3", typeof(int), typeof(DPCustomPeople), defaultMetadata);
    
        Age3Property.OverrideMetadata(typeof(DPCustomPeople), metaData);
    }

    属性元数据重写的补充

    http://www.cnblogs.com/Clingingboy/archive/2010/02/02/1661842.html 
    在之前有介绍过属性元数据的部分,这里做一个补充.上面第三点已经列出来了. 
    (1)每个DP都会有一个默认属性元数据,依赖属性默认属性元数据由内部创建,其又根据我们传入的参数,同时创建了一个属性元数据.也就是说依赖属性拥有两个属性元数据.依赖属性对于同一对象是无法重写属性元数据的.下面则报错. 

                Age3Property = DependencyProperty.Register("Age3", typeof(int), typeof(DPCustomPeople), defaultMetadata);
                Age3Property.OverrideMetadata(typeof(DPCustomPeople), metaData);
                //error

    (2)同理,由于附加属性注册时只拥有一个默认属性元数据,所以其初始化时就可以对同类型的对象进行重写(就是上面的例子) 
         注意:重写属性元数据时并不会与默认属性元数据合并,所以附加属性注册时,若有回调方法,总是会触发的

    重写属性元数据规则

    比如在重写属性元数据时重新定义了一个回调方法,其是一个合并过程,并不会覆盖父类的回调方法.如果你想改变重写规则的话可以重写

    PropertyMetadata的Merge方法,如下则不会触发父类的回调方法.这看需求而定

    public class CustomPropertyMetadata : PropertyMetadata
    {
        public CustomPropertyMetadata(object defaultValue, PropertyChangedCallback propertyChangedCallback)
        {
            this.DefaultValue = defaultValue;
            this.PropertyChangedCallback = propertyChangedCallback;
        }
        protected override void Merge(PropertyMetadata baseMetadata, DependencyProperty dp)
        {
            var a = this.PropertyChangedCallback;
            
            base.Merge(baseMetadata, dp);
            this.PropertyChangedCallback = a;
        }
    }

    改写属性元数据

    默认有两种方法为一个元素添加ToolTip

    <Button ToolTipService.ToolTip="Test">Button</Button>
    <Button ToolTip="Test">Button</Button>

    两者效果是相同的,ToolTip其内部还是设置了ToolTipService.ToolTip属性 

    public object ToolTip
    {
        get
        {
            return ToolTipService.GetToolTip(this);
        }
        set
        {
            ToolTipService.SetToolTip(this, value);
        }
    }

    注意:改写属性元数据并非改写依赖属性 
    参考:http://www.cnblogs.com/yayx/archive/2008/06/03/1213126.html

    (1)属性元数据的改写 如Control的BackgroundProperty则来自Panel的BackgroundProperty的改写,下面设置效果相同

    this.SetValue(Panel.BackgroundProperty, Brushes.Red);

    (2)改写依赖属性元数据

    ToolTipProperty = ToolTipService.ToolTipProperty.AddOwner(typeof(DPCustomPeople));

    其为设置属性的时候提供了方便,隐藏了ToolTipService的存在,其实不设置并不会怎么样. 
    如内部的ContextMenuProperty 

    ContextMenuProperty = ContextMenuService.ContextMenuProperty.AddOwner(typeof(DPCustomPeople), new FrameworkPropertyMetadata(null));

    下面两者取属性是等价的 

    public ContextMenu ContextMenu
    {
        get
        {
            return (GetValue(ContextMenuProperty) as ContextMenu);
        }
        set
        {
            SetValue(ContextMenuProperty, value);
        }
    }
    
    public ContextMenu ContextMenu
    {
        get
        {
            return ContextMenuService.GetContextMenu(this);
        }
        set
        {
            SetValue(ContextMenuService.ContextMenuProperty, value);
        }
    }


    那么改写属性元数据到底做了什么? 
    改写实质上一个重写属性元数据的过程,区别在OwnerType发生了变化,当OwnerType是继承关系的话,那么属性元数据则进行合并,否则的话则会为该OwnerType创建一个新的属性元数据.
     

    更新于:2010/8/4

    经过上面的推敲,我们可以看到依赖属性与附加属性的区别在于属性元数据的变化,附加属性也变的不再那么神奇了.下面欢迎你加入讨论中来.

    原文地址:

    http://www.cnblogs.com/Clingingboy/archive/2010/06/11/1756500.html
  • 相关阅读:
    使用 ASP.NET Core MVC 创建 Web API(五)
    使用 ASP.NET Core MVC 创建 Web API(四)
    使用 ASP.NET Core MVC 创建 Web API(三)
    使用 ASP.NET Core MVC 创建 Web API(二)
    使用 ASP.NET Core MVC 创建 Web API(一)
    学习ASP.NET Core Razor 编程系列十九——分页
    学习ASP.NET Core Razor 编程系列十八——并发解决方案
    一个屌丝程序猿的人生(九十八)
    一个屌丝程序猿的人生(九十七)
    一个屌丝程序猿的人生(九十五)
  • 原文地址:https://www.cnblogs.com/fxf568/p/2717812.html
Copyright © 2020-2023  润新知