(原创)3.2 AddOwner和OverrideMetadata的区别

9 篇文章 0 订阅

1 AddOwner和OverrideMetadata

1.1 分析

      从源代码上看,AddOwner函数中调用了OverrideMetadata, 并且把本类和依赖属性的哈希值加入到依赖属性的一张哈希列表private static Hashtable PropertyFromName中,哈希表的键值是用当前类类型的哈希值和依赖属性类类型的哈希值异或得到的,此表用来记录当前的依赖属性是属于哪个类的。

      此哈希表很重要,因为在xaml解析器中,就是利用PropertyFromName表来分析依赖属性的。看到一片博客是这样说的,PropertyFromName hashtable is mainly use by the xaml => code process(翻译:PropertyFromName hashtable 主要用来从xaml到代码的转化的处理), which can be found by analyzing the DependencyProperty.FromName() method using reflector's "use by" fuction。

      下边,我们主要介绍一下,OverrideMetadata函数是在干什么?查看源代码如下:

        public DependencyProperty AddOwner(Type ownerType, PropertyMetadata typeMetadata)
        {
            if (ownerType == null)
            {
                throw new ArgumentNullException("ownerType");
            }
            FromNameKey key = new FromNameKey(this.Name, ownerType);//获取hashcode
            lock (Synchronized)
            {
                if (PropertyFromName.Contains(key))
                {
                    throw new ArgumentException(MS.Internal.WindowsBase.SR.Get("PropertyAlreadyRegistered", new object[] { this.Name, ownerType.Name }));
                }
            }
            if (typeMetadata != null)
            {
                this.OverrideMetadata(ownerType, typeMetadata);//@1 进入OverrideMetaData
            }
            lock (Synchronized)
            {
                PropertyFromName.set_Item(key, this);//PropertyFromName hashtable
            }
            return this;
        }
在OverrideMetadata中代码嵌套多,直接写出有用的代码

            DependencyObjectType dType = DependencyObjectType.FromSystemType(forType);
            PropertyMetadata baseMetadata = this.GetMetadata(dType.BaseType);//dType.BaseType表示继承体系中的父类;例如,AnimalButton类BaseType表示的Button;获取GetMetadata的规则,可以查看《附加属性原理》的1.1.1.1中的分析:大概就是如果本继承体系中没有,就直接选取_defaultMetadata作为元数据;
            if (baseMetadata.PropertyChangedCallback != null)
            {
                Delegate[] invocationList = baseMetadata.PropertyChangedCallback.GetInvocationList();
                if (invocationList.Length > 0)
                {
                    System.Windows.PropertyChangedCallback a = (System.Windows.PropertyChangedCallback) invocationList[0];
                    for (int i = 1; i < invocationList.Length; i++)
                    {
                        a = (System.Windows.PropertyChangedCallback) Delegate.Combine(a, (System.Windows.PropertyChangedCallback) invocationList[i]);
                    }
                    a = (System.Windows.PropertyChangedCallback) Delegate.Combine(a, this._propertyChangedCallback);
                    this._propertyChangedCallback = a;
                }
            }//合并PropertyMetaChanged的事件响应
            if (this._coerceValueCallback == null)
            {
                this._coerceValueCallback = baseMetadata.CoerceValueCallback;//直接覆盖,如果提供,什么都不做
            }
            if (this._freezeValueCallback == null)
            {
                this._freezeValueCallback = baseMetadata.FreezeValueCallback;
            }

1.2 举例
        我们可以得到这样的结论,AddOwner不是依赖属性或者附加属性的专利,OverrideMetadata也不是。其中,AddOwner是OverrideMetadata和添加PropertyFromName哈希表的合集,OverrideMetadata是把当前元数据和继承体系中最近的父类的依赖属性的元数据PropertyChangedCallback进行合并,CoerceCallback进行取舍。下边附上一例:

例子: 

    public class Feeling : DependencyObject
    {
        //注册Dependency Property
        public static readonly DependencyProperty FeelProperty = DependencyProperty.Register("Feel",
    typeof(string),
    typeof(Feeling),
    new PropertyMetadata("feel-happy", new PropertyChangedCallback(FeelChanged)));

        private static void FeelChanged(object sender, DependencyPropertyChangedEventArgs e)
        {
            Trace.WriteLine("the feel is changed, new Value is " + e.NewValue.ToString());
        }

        //注册Attached Property
        public static readonly DependencyProperty SenseProperty = DependencyProperty.RegisterAttached("Sense",
            typeof(string),
            typeof(Feeling),
            new PropertyMetadata("sense-happy", new PropertyChangedCallback(SenseChanged)));
        private static void SenseChanged(object sender, DependencyPropertyChangedEventArgs e)
        {
            Trace.WriteLine("the sense is changed, new value is " + e.NewValue.ToString());
            //Window window = new Window();
            //window.Title = sender.GetType().ToString();
            //window.Show();
        }

        public static string GetSense(DependencyObject dO)
        {
            return dO.GetValue(SenseProperty) as string;
        }

        public static void SetSense(DependencyObject dO, string val)
        {
            dO.SetValue(SenseProperty, val);
        }
    }

    public class AnimalButton : Button
    {
        //在 dependency property addowner调用,propertyFromName[key] = this; //具体有什么用处
        //key = ownerType ^ propertyname
        //如果AddOwner了附加依赖属性,那么,
        public static readonly DependencyProperty SenseProperty =
            Feeling.SenseProperty.AddOwner(typeof(AnimalButton), 
            new PropertyMetadata("add owner animal button", new PropertyChangedCallback(AnimalButtonSensePropertyChanged)));
        private static void AnimalButtonSensePropertyChanged(object sender, DependencyPropertyChangedEventArgs e)
        {
            Trace.WriteLine("AnimalButton SenseProperty changed, new value is " + e.NewValue.ToString());
        }
  
        public string Sense
        {
            get
            {
                return GetValue(SenseProperty) as string;
            }
            set
            {
                SetValue(SenseProperty, value);
            }
        }

        private static void AnimalButtonFeelPropertyChanged(object sender, DependencyPropertyChangedEventArgs e)
        {
            Trace.WriteLine("AnimalButton FeelProperty Changed, new value is " + e.NewValue.ToString());
        }
        public static readonly DependencyProperty FeelProperty = Feeling.FeelProperty.AddOwner(typeof(AnimalButton),
        new PropertyMetadata(("add owner animal FeelProperty"), new PropertyChangedCallback(AnimalButtonFeelPropertyChanged)));
        public string Feel
        {
            get
            {
                return GetValue(FeelProperty) as string;
            }
            set
            {
                SetValue(FeelProperty, value);
            }
        }
    }

<Window x:Class="WpfApplication1.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:loc="clr-namespace:WpfApplication1"
        Title="MainWindow" Height="350" Width="525">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition/>
            <RowDefinition/>
        </Grid.RowDefinitions>
        <TextBox Grid.Row="0" x:Name="textBox" FontSize="32"></TextBox>
        <loc:AnimalButton Grid.Row="1" 
                          Content="Button"
                          Click="AnimalButton_Click"
                          Feel="{Binding SenseString, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type loc:MainWindow}}}"
                       Sense="{Binding SenseString, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type loc:MainWindow}}}"/>
    </Grid>

</Window>

    public partial class MainWindow : Window,INotifyPropertyChanged
    {
        public MainWindow()
        {
            InitializeComponent();
//             DependencyObjectType dt = DependencyObjectType.FromSystemType(typeof(Window));
//             DependencyObjectType dtt = DependencyObjectType.FromSystemType(typeof(AnimalButton));
//             DependencyObjectType dType = DependencyObjectType.FromSystemType(typeof(AnimalButton));
//             DependencyObjectType baseType = dType.BaseType;//获取当前依赖属性的父类中的对应的MetaData;
//             PropertyMetadata baseMetadata = Feeling.SenseProperty.GetMetadata(baseType);
//             int xx = 0;
        }

        private string _senseString = "main window";
        public string SenseString
        {
            get
            {
                return _senseString;
            }
            set
            {
                if (value != _senseString)
                {
                    _senseString = value;
                    Notify("SenseString");
                }
            }
        }

        public event PropertyChangedEventHandler PropertyChanged;
        void Notify(string name)
        {
            var handler = this.PropertyChanged;
            if (handler != null)
            {
                handler(this, new PropertyChangedEventArgs(name));
            }
        }

        private void AnimalButton_Click(object sender, RoutedEventArgs e)
        {
            SenseString = textBox.Text;
        }
    }



将会打印出

AnimalButton FeelProperty Changed, new value is RenDawei - 说明如果是依赖属性,OverrideMetadata的时候,获取的_defaultMetadata没有PropertyChanged回调函数,而附加依赖属性是有的;


the sense is changed, new value is RenDawei
AnimalButton SenseProperty changed, new value is RenDawei

在WPF(Windows Presentation Foundation)中自定义控件并绑定属性值是构建灵活用户界面的常见需求。自定义控件允许开发者封装特定的功能和外观,以便在应用程序中重复使用。绑定属性值则使得这些控件能够响应外部数据源的变化,实现数据驱动的UI更新。以下是自定义控件并绑定属性值的基本步骤: 1. 创建自定义控件类:首先,需要创建一个继承自FrameworkElement或者更高级别控件类的自定义控件类。 2. 定义依赖属性:在自定义控件类中定义依赖属性(DependencyProperty)。依赖属性是WPF中用于实现数据绑定、样式化和主题化的机制。它们允许控件的属性值由外部数据源动态决定。 3. 实现依赖属性的注册:在静态构造函数中注册你刚才定义的依赖属性。这一步是必须的,因为依赖属性需要在使用前进行注册。 4. 绑定属性值:在自定义控件的XAML标记或者代码中,使用数据绑定表达式将依赖属性与外部数据源绑定。 例如,假设我们创建了一个名为MyCustomControl的自定义控件,并想绑定一个名为"CustomProperty"的依赖属性: ```csharp public class MyCustomControl : Control { // 定义依赖属性 public static readonly DependencyProperty CustomPropertyProperty = DependencyProperty.Register( "CustomProperty", typeof(string), typeof(MyCustomControl), new PropertyMetadata(default(string))); // 依赖属性的CLR包装器 public string CustomProperty { get { return (string)GetValue(CustomPropertyProperty); } set { SetValue(CustomPropertyProperty, value); } } static MyCustomControl() { // 注册依赖属性 DefaultStyleKeyProperty.OverrideMetadata(typeof(MyCustomControl), new FrameworkPropertyMetadata(typeof(MyCustomControl))); } } ``` 在XAML中,你可以这样绑定这个属性: ```xml <MyCustomControl> <MyCustomControl.CustomProperty> <Binding Path="SomeProperty" Source="{StaticResource dataContextSource}"/> </MyCustomControl.CustomProperty> </MyCustomControl> ``` 在这里,"SomeProperty" 是绑定的数据源中的属性名,"dataContextSource" 是数据上下文的资源名称,它是一个包含数据模型实例的对象。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值