2010-02-11 9 views

Respuesta

133

La interfaz INotifyPropertyChangedes implementada con eventos. La interfaz tiene solo un miembro, PropertyChanged, que es un evento al que los consumidores pueden suscribirse.

La versión que publicó Richard no es segura. Aquí es cómo implementar esta interfaz segura:

public class MyClass : INotifyPropertyChanged 
{ 
    private string imageFullPath; 

    protected void OnPropertyChanged(PropertyChangedEventArgs e) 
    { 
     PropertyChangedEventHandler handler = PropertyChanged; 
     if (handler != null) 
      handler(this, e); 
    } 

    protected void OnPropertyChanged(string propertyName) 
    { 
     OnPropertyChanged(new PropertyChangedEventArgs(propertyName)); 
    } 

    public string ImageFullPath 
    { 
     get { return imageFullPath; } 
     set 
     { 
      if (value != imageFullPath) 
      { 
       imageFullPath = value; 
       OnPropertyChanged("ImageFullPath"); 
      } 
     } 
    } 

    public event PropertyChangedEventHandler PropertyChanged; 
} 

Tenga en cuenta que esto hace las siguientes cosas:

  • Abstracts los métodos de notificación de cambio de la propiedad para que pueda aplicar fácilmente esto a otras propiedades;

  • hace una copia de la PropertyChanged delegado antes intentar invocarlo (no hacer esto creará una condición de carrera).

  • Implementa correctamente la interfaz INotifyPropertyChanged.

Si desea , además, crear una notificación para una propiedad específica ser cambiado, puede agregar el siguiente código:

protected void OnImageFullPathChanged(EventArgs e) 
{ 
    EventHandler handler = ImageFullPathChanged; 
    if (handler != null) 
     handler(this, e); 
} 

public event EventHandler ImageFullPathChanged; 

A continuación, agregue la línea OnImageFullPathChanged(EventArgs.Empty) después de la línea OnPropertyChanged("ImageFullPath").

Ya tenemos .Net 4.5 existe la CallerMemberAttribute, que permite deshacerse de la cadena codificada por el nombre de la propiedad en el código fuente:

protected void OnPropertyChanged(
     [System.Runtime.CompilerServices.CallerMemberName] string propertyName = "") 
    { 
     OnPropertyChanged(new PropertyChangedEventArgs(propertyName)); 
    } 

    public string ImageFullPath 
    { 
     get { return imageFullPath; } 
     set 
     { 
      if (value != imageFullPath) 
      { 
       imageFullPath = value; 
       OnPropertyChanged(); 
      } 
     } 
    } 
+21

+1 por ser la única persona en este hilo hasta el momento para obtener la comprobación nula del evento correcto. –

+0

@Aaronaught: ¿Cómo conectar el método del evento con el evento ?, ¿podría explicarme? Quiero decir, ¿dónde debo escribir la implementación para el evento? – dotNETbeginner

+0

¿Por qué usar el "manejador" var local, por qué no solo if (ImageFullPathChanged! = Null) ImageFullPathChanged (esto, e); – dlchambers

5
public event EventHandler ImageFullPath1Changed; 

public string ImageFullPath1 
{ 
    get 
    { 
     // insert getter logic 
    } 
    set 
    { 
     // insert setter logic  

     // EDIT -- this example is not thread safe -- do not use in production code 
     if (ImageFullPath1Changed != null && value != _backingField) 
      ImageFullPath1Changed(this, new EventArgs(/*whatever*/); 
    } 
}       

Dicho esto, estoy completamente de acuerdo con Ryan. Este escenario es precisamente por qué existe INotifyPropertyChanged.

+3

Esta invocación de evento tiene una condición de carrera. El valor de 'ImageFullPath1Changed' puede cambiar a' null' entre el cheque y la invocación posterior. ¡No invoques eventos como este! – Aaronaught

+2

Su comprobación de nulo del evento ImageFullPath1Changed no es segura. Dado que los eventos se pueden suscribir/cancelar de forma asincrónica desde fuera de su clase, podría ser nulo después de su verificación nula y causar una NullReferenceException. En su lugar, debe tomar una copia local antes de verificar nulo. Ver la respuesta de Aaronaught. –

+0

Editado para aclarar. –

3

Si cambia su propiedad para utilizar un campo de respaldo (en lugar de una propiedad automática), puede hacer lo siguiente:

public event EventHandler ImageFullPath1Changed; 
private string _imageFullPath1 = string.Empty; 

public string ImageFullPath1 
{ 
    get 
    { 
    return imageFullPath1 ; 
    } 
    set 
    { 
    if (_imageFullPath1 != value) 
    { 
     _imageFullPath1 = value; 

     EventHandler handler = ImageFullPathChanged; 
     if (handler != null) 
     handler(this, e); 
    } 
    } 
} 
+0

Su comprobación de nulo del evento ImageFullPath1Changed no es segura. Dado que los eventos se pueden suscribir/cancelar de forma asincrónica desde fuera de su clase, podría ser nulo después de su verificación nula y causar una NullReferenceException. En su lugar, debe tomar una copia local antes de verificar nulo. Ver la respuesta de Aaronaught –

+1

@Simon P Stevens - gracias por la información. Respuesta actualizada para reflejar – Oded

+0

Agradable. Vázquez eliminado. –

7

provoca un evento, cuando una propiedad cambia es precisamente lo que hace INotifyPropertyChanged. Hay un miembro requerido para implementar INotifyPropertyChanged y ese es el evento PropertyChanged. Cualquier cosa que implemente usted mismo probablemente sea idéntica a esa implementación, por lo que no hay ventaja de no usarla.

+2

+1 a la verdad. Incluso si desea implementar un 'XChangedEvent' separado para cada propiedad, ya está haciendo el trabajo, así que continúe e implemente INotifyPropertyChanged también. El futuro (como WPF) se lo agradecerá, porque eso es lo que el futuro espera que haga. –

27

utilizo en gran medida los mismos patrones que Aaronaught, pero si usted tiene una gran cantidad de propiedades que podría ser agradable de usar un poco de magia método genérico para hacer su código un poco más DRY

public class TheClass : INotifyPropertyChanged { 
    private int _property1; 
    private string _property2; 
    private double _property3; 

    protected virtual void OnPropertyChanged(PropertyChangedEventArgs e) { 
     PropertyChangedEventHandler handler = PropertyChanged; 
     if(handler != null) { 
      handler(this, e); 
     } 
    } 

    protected void SetPropertyField<T>(string propertyName, ref T field, T newValue) { 
     if(!EqualityComparer<T>.Default.Equals(field, newValue)) { 
      field = newValue; 
      OnPropertyChanged(new PropertyChangedEventArgs(propertyName)); 
     } 
    } 

    public int Property1 { 
     get { return _property1; } 
     set { SetPropertyField("Property1", ref _property1, value); } 
    } 
    public string Property2 { 
     get { return _property2; } 
     set { SetPropertyField("Property2", ref _property2, value); } 
    } 
    public double Property3 { 
     get { return _property3; } 
     set { SetPropertyField("Property3", ref _property3, value); } 
    } 

    #region INotifyPropertyChanged Members 

    public event PropertyChangedEventHandler PropertyChanged; 

    #endregion 
} 

Por lo general también hacen el método OnPropertyChanged virtual para permitir que las subclases para anular a coger los cambios de propiedad.

+9

Ahora con .NET 4.5 incluso puede obtener el nombre de propiedad de forma gratuita utilizando la propiedad CallerMemberNameAttribute http://msdn.microsoft.com/en-us/library/system.runtime.compilerservices.callermembernameattribute.aspx. – fsimonazzi

+0

Esto está funcionando genial. Gracias por el ejemplo. +1 para el CallerMemberName – curob

Cuestiones relacionadas