WPF自定义DependencyProperty通知更改

时间:2010-05-18 09:35:22

标签: wpf dependency-properties notify dependencyobject

我有一个名为MyComponent的类,它有一个DependencyProperty caled BackgroundProperty。

public class MyComponent
{
    public MyBackground Background
    {
        get { return (MyBackground)GetValue(BackgroundProperty); }
        set { SetValue(BackgroundProperty, value); }
    }
    public static readonly DependencyProperty BackgroundProperty =
        DependencyProperty.Register("Background", typeof(MyBackground),
            typeof(MyComponent), new FrameworkPropertyMetadata(default(MyBackground), new PropertyChangedCallback(OnPropertyChanged)));
}

MyBackground是一个派生自DependencyObject的类,它有一些DependencyProperties。

public class MyBackground : DependencyObject
{
    public Color BaseColor
    {
        set { SetValue(BaseColorProperty, value); }
        get { return (Color)GetValue(BaseColorProperty); }
    }
    public static readonly DependencyProperty BaseColorProperty =
        DependencyProperty.Register("BaseColor", typeof(Color),
            typeof(MyBackground ), new UIPropertyMetadata(Colors.White));

    [...]
}

现在,我想要的是当MyBackground中的属性被更改时,MyComponent将被通知MyBackground已经更改,并且名为OnPropertyChanged的PropertyChangedCallback被调用。

4 个答案:

答案 0 :(得分:3)

请耐心等待一秒钟,因为看起来你正试图违背WPF的规定。由于您似乎正在编写与显示逻辑相关的代码,因此使相关DependencyObject彼此交互的典型方法是通过绑定。

例如,如果MyComponent是某种控件并且在Background中使用ControlTemplate属性,那么您将使用引用TemplateBinding的{​​{1}} {1}}属性和任何重要的子属性。

从1)您可能已经知道并且2)您要么没有使用模板,要么没有它们可用,您可以在代码中设置绑定以响应{{1}的更改属性。如果您提供有关Background方法的详细信息,我可以提供一些示例代码。

答案 1 :(得分:3)

执行所描述的一种方法是从Freezable而不是DependencyObject派生。当Freezable的属性更改任何DO引用的PropertyChangedCallback时,将调用Freezable,以便调用MyComponent的Background属性。在这种情况下,e.OldValue和e.NewValue将是相同的参考。内部WPF在事件args上有一些标志,表明它是一个子对象更改。

这就是框架对画笔这样的东西所做的事情,这样如果改变SolidColorBrush的Color属性,元素就会失效。如果一个对象永远不会被改变(或者你想让它保持线程安全),那么就可以冻结对象(即使它不可变)。

BTW我可能会避免使用Background作为属性的名称。大多数开发人员都会假设它是Brush类型,因为框架在其几个元素(例如控件,边框)上使用该命名属性。

答案 2 :(得分:2)

听起来好像要使用DependencyPropertyDescriptor和AddValueChanged。

以下是一篇文章:http://www.codeproject.com/Articles/34741/Change-Notification-for-Dependency-Properties.aspx

..可能还有更好的实施方式:http://agsmith.wordpress.com/2008/04/07/propertydescriptor-addvaluechanged-alternative/

答案 3 :(得分:0)

这是我为WPF编写的一小类静态扩展方法 - 它允许您为任何DependencyObject上的任何DependencyProperty更改注册EventHandler或Action回调。不需要更改依赖项对象。

它还可以防止递归(即,如果你在回调期间改变了同一个属性,等等。)

它利用了@ScottBilas链接到的DependencyPropertyDescriptor。

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Windows;

namespace BrainSlugs83.Writes.Too.Much.Code
{
    public static class WpfExtensions
    {
        public static void OnPropertyChanged<T>(this T obj, DependencyProperty prop, Action<T> callback) where T : DependencyObject
        {
            if (callback != null)
            {
                obj.OnPropertyChanged(prop, new EventHandler((o, e) =>
                {
                    callback((T)o);
                }));
            }
        }

        public static void OnPropertyChanged<T>(this T obj, DependencyProperty prop, EventHandler handler) where T : DependencyObject
        {
            var descriptor = DependencyPropertyDescriptor.FromProperty(prop, typeof(T));
            descriptor.AddValueChanged(obj, new EventHandler((o, e) =>
            {
                if (handler != null)
                {
                    if (o == null) { handler(o, e); }
                    else
                    {
                        lock (PreventRecursions)
                        {
                            if (IsRecursing(obj, prop)) { return; }
                            SetIsRecursing(obj, prop, true);
                        }

                        try
                        {
                            handler(o, e);
                        }
                        finally
                        {
                            SetIsRecursing(obj, prop, false);
                        }
                    }
                }
            }));
        }

        #region OnPropertyChanged Recursion Prevention

        private static readonly Dictionary<object, List<DependencyProperty>> PreventRecursions = new Dictionary<object, List<DependencyProperty>>();

        private static bool IsRecursing(object obj, DependencyProperty prop)
        {
            lock (PreventRecursions)
            {
                List<DependencyProperty> propList = null;
                if (PreventRecursions.ContainsKey(obj))
                {
                    propList = PreventRecursions[obj];
                }

                return propList == null ? false : propList.Contains(prop);
            }
        }

        private static void SetIsRecursing(object obj, DependencyProperty prop, bool value)
        {
            lock (PreventRecursions)
            {
                List<DependencyProperty> propList = null;
                if (PreventRecursions.ContainsKey(obj))
                {
                    propList = PreventRecursions[obj];
                }

                if (propList == null)
                {
                    if (!value) { return; }

                    propList = PreventRecursions[obj] = new List<DependencyProperty>();
                }

                if (value)
                {
                    if (!propList.Contains(prop))
                    {
                        propList.Add(prop);
                    }
                }
                else
                {
                    while (propList.Contains(prop))
                    {
                        propList.Remove(prop);
                    }

                    if (!propList.Any())
                    {
                        propList = PreventRecursions[obj] = null;
                    }
                }
            }
        }

        #endregion

        public static bool IsInDesignMode(this DependencyObject obj)
        {
            try
            {
                return DesignerProperties.GetIsInDesignMode(obj);
            }
            catch { /* do nothing */ }

            return false;
        }
    }
}