2012-08-08 21 views
10

que tengo una clase Persona:Ejecución INotifyPropertyChanged de propiedades anidadas

public class Person : INotifyPropertyChanged 
{ 
    private string _name; 
    public string Name{ 
    get { return _name; } 
    set { 
      if (_name != value) { 
      _name = value; 
      OnPropertyChanged("Name"); 
      } 
    } 

    private Address _primaryAddress; 
    public Address PrimaryAddress { 
    get { return _primaryAddress; } 
    set { 
      if (_primaryAddress != value) { 
      _primaryAddress = value; 
      OnPropertyChanged("PrimaryAddress"); 
      } 
    } 

    //OnPropertyChanged code goes here 
} 

tengo una clase Dirección:

public class Address : INotifyPropertyChanged 
{ 
    private string _streetone; 
    public string StreetOne{ 
    get { return _streetone; } 
    set { 
      if (_streetone != value) { 
      _streetone = value; 
      OnPropertyChanged("StreetOne"); 
      } 
    } 

    //Other fields here 

    //OnPropertyChanged code goes here 
} 

tengo un modelo de vista:

public class MyViewModel 
{ 
    //constructor and other stuff here 

    private Person _person; 
    public Person Person{ 
    get { return _person; } 
    set { 
      if (_person != value) { 
      _person = value; 
      OnPropertyChanged("Person"); 
      } 
    } 

} 

Tengo una visión que tiene las siguientes líneas:

<TextBox Text="{Binding Person.Name, Mode=TwoWay, 
    UpdateSourceTrigger=PropertyChanged /> 

<TextBox Text="{Binding Person.Address.StreetOne, Mode=TwoWay, 
    UpdateSourceTrigger=PropertyChanged /> 

Ambos valores aparecen en el cuadro de texto cuando se carga la vista.

Los cambios en el primer cuadro de texto dispararán OnPropertyChanged("Person") en MyViewModel. Estupendo.

Los cambios en el segundo cuadro de texto ("Person.Address.StreetOne") NO activa OnPropertyChanged("Person") dentro de MyViewModel. Lo que significa que no llama al método SET del objeto Persona. No es bueno. Curiosamente, se llama al método SET de StreetOne dentro de la clase Address.

¿Cómo se obtiene el método SET del objeto Persona dentro del modelo de vista cuando se cambia Person.Address.StreetOne ???

¿Debo aplanar mis datos para que SteetOne esté dentro de Person and not Address?

Gracias!

+1

@Revious ... Con respecto a su generosidad, la pregunta ya ha sido respondida. Entonces, ¿qué tipo de respuesta estás buscando aquí? –

+0

@ S. Akbari: la respuesta aceptada es una solución que está aplanando la propiedad ..Estaba buscando una solución real – Revious

+1

@Revious Agregué una respuesta con la alternativa estándar que es propagar los cambios desde el niño al padre utilizando un controlador de eventos. No estoy seguro de cómo se pasó por alto en las respuestas originales, ya que es bastante estándar y una solución "real". –

Respuesta

8

si desea que el SET modelo de vista que se llamará usted podría crear una propiedad de la calle

public class MyViewModel 
{ 
    //constructor and other stuff here 
    public string Street{ 
    get { return this.Person.PrimaryAddress.StreetOne; } 
    set { 
     if (this.Person.PrimaryAddress.StreetOne!= value) { 
     this.Person.PrimaryAddress.StreetOne = value; 
     OnPropertyChanged("Street"); 
     } 
    } 

} 

xaml

<TextBox Text="{Binding Street, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged /> 

pero esta solución tiene sus inconvenientes. voy con la respuesta de Reeds en mis proyectos

+0

Gracias por la respuesta. De esta manera funciona (no puedo votar aún o lo haría). Supongo que se trata de un debate sobre si hacer esto o sobrecargar mi clase de persona es una mejor opción. Me gustaría ir con la respuesta de Reeds, pero no estoy seguro de lo que quiere decir con "tendrás que suscribirte a los eventos PropertyChanged manualmente". Ya implementé OnPropertyChanged en los niveles de Dirección, Persona y MyViewModel ... –

+0

Al final, prácticamente fui por esta ruta. Lo anterior funciona, pero en lugar de colocar el código anterior en el modelo de vista, lo agregué a la clase Person. Así que básicamente tuve una picadura llamada SreetOneDisplay. Así que realmente no aplané mi modelo, todavía hay una clase de dirección. –

4

¿Cómo puedo hacer que se llame al método SET del objeto Person dentro de ViewModel cuando se cambia Person.Address.StreetOne ???

¿Por qué quieres hacer esto? No debe requerirse; solo necesita el evento StreetOne propiedad cambiada para disparar.

¿Necesito aplanar mis datos para que SteetOne esté dentro de Person and not Address ??

Si realmente desea que esto se dispare, no necesita aplanarlo (aunque esa es una opción). Puede suscribirse al evento Address's PropertyChanged dentro de su clase Person, y plantear el evento para "Dirección" dentro de Person cuando cambie. Esto no debería ser necesario, sin embargo.

+0

¡Gracias por la respuesta! Déjame intentar aclarar. Dentro de MyViewModel no ocurre nada cuando se cambia el cuadro de texto '("Person.Address.StreetOne")'. Me gustaría que el método SET de Person se active de la misma manera que cuando se cambia Person.Name. Además, aunque se llama al método SET de StreetOne dentro de la clase Address, no parece haber nada cuando se llama a 'OnPropertyChanged (' SteetOne ')'. El método SET de PrimaryAddress dentro de la clase Person no se llama. –

+1

@lloydchristmas ¿Por qué quieres que se llame, sin embargo? WPF no requiere que esto sea llamado. Si desea que se invoque, deberá suscribirse a los eventos PropertyChanged manualmente ... –

+0

Realizo algunas otras llamadas de validación cuando se llama al método SET de Person. Entonces, cada vez que la persona cambia, ejecuto algunas validaciones sobre ella. Por ejemplo, cuando se cambia StreetOne, necesito verificar que sea una dirección válida ... cosas así. Entonces, ¿cómo me suscribo manualmente al evento PropertChanged? Gracias. –

0

Hay un error de ortografía en su notificación de cambio de la propiedad:

OnPropertyChanged("SteetOne");

debería ser

OnPropertyChanged("StreetOne");

+0

Gracias, lo arreglé. No es el problema sin embargo. Realmente esto fue solo un ejemplo que inventé. –

1

Como no pude encontrar una solución lista para usar, hice una implementación personalizada basada en las sugerencias de Pieters (y Marks) (¡gracias!).

Usando las clases, usted será notificado de cualquier cambio en un árbol de objetos profundo, esto funciona para cualquier INotifyPropertyChanged Tipos de aplicación y INotifyCollectionChanged colecciones * ejecución (Obviamente, estoy usando el ObservableCollection para eso).

Espero que esta sea una solución bastante limpia y elegante, aunque no está completamente probada y hay espacio para mejoras. Es bastante fácil de usar, basta con crear una instancia de ChangeListener utilizando su Create método estático y pasar su INotifyPropertyChanged:

var listener = ChangeListener.Create(myViewModel); 
listener.PropertyChanged += 
    new PropertyChangedEventHandler(listener_PropertyChanged); 

la PropertyChangedEventArgs proporcionan una PropertyName que será siempre el "camino" lleno de sus objetos. Por ejemplo, si cambia el nombre de "BestFriend" de su Persona, el PropertyName será "BestFriend.Name", si el BestFriend tiene una colección de Children y usted cambia su edad, el valor será "BestFriend.Children []. Age" y así. No se olvide de Dispose cuando se destruye su objeto, entonces (con suerte) se dará de baja completamente de todos los oyentes del evento.

Compila en .NET (probado en 4) y Silverlight (probado en 4). Debido a que el código en separó en tres clases, que acabo de publicar el código para gist 705450 donde podrá tomar todo: https://gist.github.com/705450 **

*) Una de las razones de que el código está trabajando es que el ObservableCollection también implementa INotifyPropertyChanged, de lo contrario no funcionaría como se desee, esto es una advertencia conocida

**) utilizar de forma gratuita, publicada bajo MIT License

4

Mientras que la adición 'de paso' propiedades a su modelo de vista es una buena solución, que puede convertirse rápidamente en insostenible. La alternativa estándar es propagar los cambios de la siguiente manera:

public Address PrimaryAddress { 
    get { return _primaryAddress; } 
    set { 
      if (_primaryAddress != value) 
      { 
      //Clean-up old event handler: 
      if(_primaryAddress != null 
       _primaryAddress.PropertyChanged -= AddressChanged; 

      _primaryAddress = value; 

      _primaryAddress.PropertyChanged += AddressChanged; 

      OnPropertyChanged("PrimaryAddress"); 
      } 
    } 

    private void AddressChanged(object sender, PropertyChangedEventArgs args) 
    { 
     OnPropertyChanged("PrimaryAddress"); 
    } 

Ahora las notificaciones de cambio se propagan de la dirección a la persona.

Cuestiones relacionadas