2010-05-18 3 views
9

У меня есть класс MyComponent, и у него есть DependencyProperty caled BackgroundProperty.WPF custom DependencyProperty уведомляет об изменениях

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 называться.

+0

Я немного смущен, почему вам это нужно. Обычно это наоборот, где DP используются для привязок, и когда они меняются, вы хотите уведомить DP. Зачем вам это нужно наоборот? –

+0

Что значит это назад @Omribitan? Это стандартный WPF. Если я изменяю значение свойства зависимостей, все вещи, связанные с этим свойством, знают об этом мгновенно. Это своего рода свойства зависимостей - и привязка данных WPF построена на этой концепции. – BrainSlugs83

+0

@ BrainSlugs83 Подумайте о видимости элемента управления, ограниченном свойством класса ViewModel, назовем его 'IsVisibile'. «Видимость» - это DP, а «IsVisibile» - простое свойство. Что обычно происходит, когда изменения «IsVisible» вы хотите уведомить пользовательский интерфейс (в основном с помощью «INotifyPropertyChanged»), чтобы «DP» знал, что значение изменилось, а не наоборот ... –

ответ

3

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

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

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

3

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

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

BTW Я бы, вероятно, избегал использования Background в качестве имени свойства. Большинство разработчиков предположит, что это тип Brush, поскольку это то, что использует инфраструктура для этого именованного свойства на нескольких его элементах (например, управление, граница).

0

Вот небольшой статический класс методов расширения, который я написал для 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; 
     } 
    } 
}