WPF пользовательские DependencyProperty уведомлять об изменениях - PullRequest
9 голосов
/ 18 мая 2010

У меня есть класс с именем MyComponent, и у него есть DependencyProperty с именем 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 и вызывал PropertyChangedCallback с именем OnPropertyChanged.

Ответы [ 4 ]

3 голосов
/ 14 октября 2013

Потерпи меня секунду, потому что кажется, что ты пытаешься пойти против зерна WPF. Поскольку кажется, что вы пишете код, связанный с логикой отображения, типичным способом взаимодействия связанных DependencyObject s друг с другом является привязка.

Если, например, MyComponent - это какой-то элемент управления и он использует свойство Background в своем ControlTemplate, вы должны использовать TemplateBinding, который ссылается на свойство Background и любые важные подчиненные элементы. свойства.

Поскольку 1) вы, вероятно, уже знаете это и 2) вы либо не используете шаблоны, либо не имеете их в наличии, вы можете установить привязку в коде, чтобы реагировать на изменения в свойстве Background , Если вы предоставите более подробную информацию о том, что делает ваш метод OnPropertyChanged, я могу предоставить пример кода.

3 голосов
/ 17 октября 2013

Один из способов сделать то, что вы описываете, - получить Freezable вместо DependencyObject. Когда свойство Freezable изменяет PropertyChangedCallback для любого DO, ссылающегося на Freezable, будет вызываться обратный вызов для свойства Background вашего MyComponent. В этом случае e.OldValue и e.NewValue будут одной и той же ссылкой. Внутренне WPF имеет некоторый флаг в аргументах события, который указывает, что это изменение подобъекта.

Это то, что делает каркас для таких вещей, как кисти, так что элемент может быть признан недействительным, если, скажем, свойство Color объекта SolidColorBrush изменено. Если объект никогда не будет изменен (или вы хотите сделать его потокобезопасным), тогда можно заморозить объект (т.е. сделать его неизменным).

Кстати, я бы, вероятно, не использовал Background в качестве имени свойства. Большинство разработчиков предполагают, что это тип Brush, поскольку именно это среда использует для этого именованного свойства на нескольких его элементах (например, control, border).

2 голосов
/ 30 июля 2010

Похоже, вы хотите использовать DependencyPropertyDescriptor и AddValueChanged.

Вот статья об этом: http://www.codeproject.com/Articles/34741/Change-Notification-for-Dependency-Properties.aspx

.. и, возможно, лучшая реализация: http://agsmith.wordpress.com/2008/04/07/propertydescriptor-addvaluechanged-alternative/

0 голосов
/ 25 ноября 2013

Вот небольшой статический класс методов расширения, которые я написал для WPF - он позволяет зарегистрировать EventHandler или обратный вызов Action для изменения любого DependencyProperty в любом DependencyObject. Никаких изменений в объекте зависимостей не требуется.

Он также предотвращает рекурсию (т. Е. Если вы измените то же свойство во время обратного вызова и т. Д.)

Используется преимущество DependencyPropertyDescriptor, с которым @ScottBilas связан.

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;
        }
    }
}
...