2010-05-18 10 views
9

Tengo una clase llamada MyComponent y tiene una PropertyProject de DependencyProperty caled.WPF personalizado DependencyProperty notify changes

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 es una clase que deriva de DependencyObject y tiene algunas 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)); 

    [...] 
} 

Ahora, lo que quiero decir cuando se cambia una propiedad de MyBackground, MyComponent a ser notificado de que MyBackground ha cambiado y el PropertyChangedCallback llamado OnPropertyChanged a ser llamado.

+0

Estoy un poco confundido sobre por qué lo necesitas. Por lo general, es al revés, donde se usan DP para enlaces y cuando cambian, desea notificar al DP. ¿Por qué lo necesitarías al revés? –

+0

¿Qué quieres decir con esto es hacia atrás @Orribitan? Esto es WPF estándar. Si modifico el valor de una propiedad de dependencia, todas las cosas ligadas a esa propiedad lo sabrán al instante. Este es el tipo de propiedades de dependencia, y el enlace de datos de WPF se basa en este concepto. – BrainSlugs83

+0

@ BrainSlugs83 Piense en la visibilidad de un control limitada a una propiedad en la clase del ViewModel, llamémosle 'IsVisibile'. el 'Visibility' es el DP y el' IsVisibile' es una propiedad simple. Lo que generalmente sucede es cuando cambios 'IsVisible' quieres notificar a la UI (sobre todo usando 'INotifyPropertyChanged') para que el 'DP' sepa que se cambió el valor, y no al revés ... –

Respuesta

3

oso conmigo por un segundo porque parece que usted está tratando de ir contra la corriente de WPF. Dado que parece que está escribiendo código relacionado con la lógica de visualización, el método típico para obtener DependencyObject s relacionados para interactuar entre sí es a través de enlaces.

Si, por ejemplo, MyComponent es un control de algún tipo y se utiliza la propiedad Background en su ControlTemplate, se utilizaría un TemplateBinding que hace referencia a la propiedad Background y cualquier sub-propiedades importantes.

Dado que 1) probablemente ya lo sepa y 2) o no está utilizando plantillas o no las tiene disponibles, puede configurar un enlace en el código para reaccionar a los cambios en la propiedad Background. Si proporciona más detalles sobre el método OnPropertyChanged, puedo proporcionar algún código de muestra.

3

Una forma de hacer lo que describes sería derivar de Freezable en lugar de DependencyObject. Cuando una propiedad de un Freezable cambia el PropertyChangedCallback para cualquier referencia de DO, se invocará a Freezable para que se devuelva la devolución de la propiedad Background de MyComponent. En ese caso, e.OldValue y e.NewValue serán la misma referencia. Internamente, WPF tiene algún indicador en los argumentos del evento que indica que se trata de un cambio de subobjeto.

Esto es lo que hace el marco para cosas como pinceles, por lo que un elemento puede ser invalidado si, por ejemplo, se cambia la propiedad Color de un SolidColorBrush. Si un objeto nunca se cambiará (o si quiere que sea seguro para hilos), entonces se puede congelar el objeto (es decir, hacerlo inmutable).

BTW Probablemente evitaría usar Background como el nombre de la propiedad. La mayoría de los desarrolladores supondrán que es de tipo Pincel ya que es lo que el marco utiliza para esa propiedad con nombre en varios de sus elementos (por ejemplo, control, borde).

0

Aquí hay una pequeña clase estática de métodos de extensión que escribí para WPF: le permite registrar un EventHandler o una devolución de llamada de Acción para cambiar cualquier DependencyProperty en cualquier DependencyObject. No son necesarios cambios en el objeto de dependencia.

También evita la repetición (es decir, si se cambia esa misma propiedad durante la devolución de llamada, etc ..)

Se aprovecha de la DependencyPropertyDescriptor que @ScottBilas vinculados.

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; 
     } 
    } 
} 
Cuestiones relacionadas