none
notificar cambio de propiedad de una clase RRS feed

  • Pregunta

  • Saludos a todos,

    Alguien me puede ayudar en lo siguiente: Necesito saber si ha cambiado el valor de una propiedad de una clase "X", esta notificacion se le tiene que avisar a una clase "Y" que es donde se hace una instancia de la clase "X".Les agradeceria cualquier comentario, saludos!!!

    lunes, 12 de septiembre de 2011 20:03

Respuestas

  • hola

    no has evaluado suar eventos ?

    digo en la propeiad al realziar el set, podria lancer un evento al cual se deberia registrar la clase Y

    la clase X es quien define el evento y quien lo dispara, la Y es la que se subscribe

    saludos


    Leandro Tuttini

    Blog
    Buenos Aires
    Argentina
    lunes, 12 de septiembre de 2011 20:09
  • Hola,

    Realmente quien hace todo es BindingSource y Binding. Esos son los responsables de transmitir la información entre Persona y Form3.

    Para explicarlo hace falta un rio de tinta y yo menos William_Shakespeare soy cualquier cosa :).

     

    Pero voy a intertar exponerlo. Cuando haces binding lo normal es que los datos vayan en las dos direcciones, es decir desde la propiedad del  control a la propiedad de la clase y desde la propiedad de la clase a la propiedad a la propiedad del Control.

    En Ambos casos se necesita utilizar System.Reflection la verdad que un gran amigo, recuerdo las criticas de hace años y sobre todo cuando te tomaban por loco hace más CallByName

    1. Desde la propiedad de la clase a la propiedad del Control

    Internamente Binding Y BindingSource hacer algo parecido a esto.

     

    PropertyInfo origen = this._Persona.GetType().GetProperty("Nombre");
    
                if (origen != null)
                {
                    PropertyInfo destino = this.textBox1.GetType().GetProperty("Text");
                    if (destino != null)
                    {
                        object Valor = origen.GetValue(_Persona, null);
                        destino.SetValue(this.textBox1, Valor, null);
                    }
                }
                
    

     


    Fijate que utilizo Text, no reinventes, todos hemos pasado por eso y las consecuencias suelen ser nefastas.

    2.Desde la propiedad del Control a la Propiedad de la Clase.

    Esto es un poco más complicado, pero básicamente esta basado en algo parecido. Es más complicado porque cualquier objeto implementa ToString() y por tanto es facil convertir de cualquier valor a string que en definitiva es lo que ves en la propiedad Text del Control.

    Para realizar el mecanismo Propiedad->Control To Clase->Propiedad tienes que utilizar  TypeConverter. Es logico tienes que convertir, por ejemplo un "1" to int32, que es el tipo que has definido en la clase.

     

    Basicamente este paso esta basado en el control del evento "Validating", "EventDescriptorCollection" y "EventDescriptor"

     

    Paso el código interno de Binding para que veas lo que te comento.

     

     

    private void CheckBinding()
    {
        this.bindToObject.CheckBinding();
        if ((this.control == null) || (this.propertyName.Length <= 0))
        {
            this.propInfo = null;
            this.validateInfo = null;
        }
        else
        {
            PropertyDescriptorCollection properties;
            this.control.DataBindings.CheckDuplicates(this);
            Type componentType = this.control.GetType();
            string b = this.propertyName + "IsNull";
            PropertyDescriptor descriptor = null;
            PropertyDescriptor descriptor2 = null;
            InheritanceAttribute attribute = (InheritanceAttribute) TypeDescriptor.GetAttributes(this.control)[typeof(InheritanceAttribute)];
            if ((attribute != null) && (attribute.InheritanceLevel != InheritanceLevel.NotInherited))
            {
                properties = TypeDescriptor.GetProperties(componentType);
            }
            else
            {
                properties = TypeDescriptor.GetProperties(this.control);
            }
            for (int i = 0; i < properties.Count; i++)
            {
                if ((descriptor == null) && string.Equals(properties[i].Name, this.propertyName, StringComparison.OrdinalIgnoreCase))
                {
                    descriptor = properties[i];
                    if (descriptor2 != null)
                    {
                        break;
                    }
                }
                if ((descriptor2 == null) && string.Equals(properties[i].Name, b, StringComparison.OrdinalIgnoreCase))
                {
                    descriptor2 = properties[i];
                    if (descriptor != null)
                    {
                        break;
                    }
                }
            }
            if (descriptor == null)
            {
                throw new ArgumentException(SR.GetString("ListBindingBindProperty", new object[] { this.propertyName }), "PropertyName");
            }
            if (descriptor.IsReadOnly && (this.controlUpdateMode != ControlUpdateMode.Never))
            {
                throw new ArgumentException(SR.GetString("ListBindingBindPropertyReadOnly", new object[] { this.propertyName }), "PropertyName");
            }
            this.propInfo = descriptor;
            Type propertyType = this.propInfo.PropertyType;
            this.propInfoConverter = this.propInfo.Converter;
            if (((descriptor2 != null) && (descriptor2.PropertyType == typeof(bool))) && !descriptor2.IsReadOnly)
            {
                this.propIsNullInfo = descriptor2;
            }
            EventDescriptor descriptor3 = null;
            string str2 = "Validating";
            EventDescriptorCollection events = TypeDescriptor.GetEvents(this.control);
            for (int j = 0; j < events.Count; j++)
            {
                if ((descriptor3 == null) && string.Equals(events[j].Name, str2, StringComparison.OrdinalIgnoreCase))
                {
                    descriptor3 = events[j];
                    break;
                }
            }
            this.validateInfo = descriptor3;
        }
        this.UpdateIsBinding();
    }
    
     
    
     
    
    

    Saludos,

     

     

     


    phurtado
    martes, 13 de septiembre de 2011 23:06
    Moderador

Todas las respuestas

  • Hola

    Podrías explicarte con un ejemplo?

    lunes, 12 de septiembre de 2011 20:07
  • hola

    no has evaluado suar eventos ?

    digo en la propeiad al realziar el set, podria lancer un evento al cual se deberia registrar la clase Y

    la clase X es quien define el evento y quien lo dispara, la Y es la que se subscribe

    saludos


    Leandro Tuttini

    Blog
    Buenos Aires
    Argentina
    lunes, 12 de septiembre de 2011 20:09
  • Hola Cruznick, Leandro

    Gracias por sus comentarios, tendria que declarar un

    public

     

    event EventHandler MiEvento;

    Y lanzarlo en el set de la propiedad de la clase "X" y hacer el manejo en la clase "Y" de la siguiente manera:

    X.MiEvento += new EventHandler(evento);

    private void evento(object sender, EventArgs e)

    {

    }

     

    Les agradeceria cualquier comentario, saludos!!!

     
    lunes, 12 de septiembre de 2011 21:28
  • Hola,

    No inventes nada que está implementado en el propio framework, mira este link

    http://msdn.microsoft.com/en-us/library/system.componentmodel.inotifypropertychanged(v=vs.85).aspx

     

    Esto practimamente es un estandar en el desarrollo .net.

    Saludos,


    phurtado
    lunes, 12 de septiembre de 2011 22:54
    Moderador
  • pero basicamente es lo mismo, un evento

    solo que lo hace mas estandar el hecho de implementar una interfaz y dar un marco de trabajo

     

    saludos


    Leandro Tuttini

    Blog
    Buenos Aires
    Argentina
    martes, 13 de septiembre de 2011 0:32
  • claro hasta ahi va bien

    solo que no ahs mostrado dodne pones cada parte

    imagino la linea

    public     event EventHandler MiEvento;

    esta dentro de la clase X

     

    el resto del codigo lo has puesto dentro de cla clase Y que se adjunta para recibir el evento

     

    lanzarlo en el set de la propiedad de la clase "X"

    exacto

     

    saludos


    Leandro Tuttini

    Blog
    Buenos Aires
    Argentina
    martes, 13 de septiembre de 2011 0:35
  • Hola Leandro,

    Efectivamente es lo mismo un evento, pero implementado por medio de la interfaz INotifyPropertyChanged, que como tu bien sabes es la interfaz que testean todos los mecanismos de binding de .net, con lo cual para que crear un evento o inventar algo que ya existe y que puedo utilizar, no solo para lo que quiere @luigi88, sino para muchas otras cosas.

     

    Por eso pase ese link.

     

    Saludos,


    phurtado
    martes, 13 de septiembre de 2011 8:28
    Moderador
  • Hola @luigi88.

    Efectivamente con un evento puedes hacer lo que quieres, pero quiero que te fijes en el siguiente código.

    1. Defino una clase persona.

    2. Dibujo un TextBox con nombre textBox1

    3. Asocio el formulario a datos por medio de un BindingSoure.

    4. Quiero que mi propiedad Nombre se enlace a la propiedad Text del textBox1.

    5. Por último quiero que cuando una propiedad de la clase Persona se modifique, mi interfaz también lo haga.

    Fijate en el siguiente código, cuando pulso en el boton no se actualiza la interface, puesto que no se implementa INotifyPropertyChanged.

    public partial class Form3 : Form
        {
            Persona _Persona;
            BindingSource _PersonaBindingSource;
            public Form3()
            {
                InitializeComponent();
    
                _Persona = new Persona() { Nombre = "Hola soy la persona" };
                _PersonaBindingSource = new BindingSource(this.components);
                
                ISupportInitialize Inicialize = _PersonaBindingSource;
                
                Inicialize.BeginInit();
                _PersonaBindingSource.DataSource = typeof(Persona);
                Inicialize.EndInit();
    
                this.personaBindingSource.DataSource = _Persona;
                
              
                
            }
    
            private void button1_Click(object sender, EventArgs e)
            {
                _Persona.Nombre = "Nuevo Nombre";
            }
    
            
        }
        public class Persona
        {
            private string _Nombre;
            public event PropertyChangedEventHandler PropertyChanged;
            public string Nombre
            {
                get { return this._Nombre; }
                set
                {
                    if (this.Nombre != value)
                    {
                        this._Nombre = value;
                        this.OnPropertyChanged("Nombre");
                    }
                }
            }
            protected virtual void OnPropertyChanged(string Propiedad)
            {
                if (this.PropertyChanged != null)
                    this.PropertyChanged(this, new PropertyChangedEventArgs(Propiedad));
            }
        }
    


    Ahora cambia la clase persona por el siguiente código

    public class Persona:INotifyPropertyChanged
        {
            private string _Nombre;
            public string Nombre
            {
                get { return this._Nombre; }
                set
                {
                    if (this.Nombre != value)
                    {
                        this._Nombre = value;
                        this.OnPropertyChanged("Nombre");
                    }
                }
            }
            protected virtual void OnPropertyChanged(string Propiedad)
            {
                if (this.PropertyChanged!=null)
                    this.PropertyChanged(this,new PropertyChangedEventArgs(Propiedad));
            }
            public event PropertyChangedEventHandler PropertyChanged;
        }
    


    Como puedes ver ahora si se modifica la interfaz de usuario cuando se pulsa en el boton y cambia la propiedad Nombre de la clase Persona.

    Esto es aplicable a WindowsForms,WPF,Silverlight,etc. En definitiva lo que tienes que hacer para este tipo de cosas es escribir tus clases tal y como te muestro en el último ejemplo, implementando INotifyPropertyChanged. De esa forma puedes hacer lo que pretendías hacer que es saber cuando una propiedad cambia en una clase y adaptar tus clases al mecanismo de Binding de .Net, es decir 2 por 1 con el mismo código.

    Saludos.


    phurtado
    martes, 13 de septiembre de 2011 10:07
    Moderador
  • Hola Leandro, Pedro Hurtado

    Les agradezco sus comentarios, te comento Leandro que tu suposicion es correcta al mencionar exactamente donde la declaracion del evento, donde se lanza (clase "X") y como se implementa en la clase "Y".

    Te comento Pedro Hurtado que sobre el ejemplo de la liga

    http://msdn.microsoft.com/en-us/library/system.componentmodel.inotifypropertychanged(v=vs.85).aspx

    y el ejemplo que mencionas arriba tengo una pregunta, como se le notifica a la clase Form3 que ha cambiado el valor de la propiedad Nombre de la clase Persona?.

    Por eso en primera instancia me fui por el camino con eventos como lo menciona Leandro.

    La clase "X" es un user control que contiene un TextBox, tambien tiene una propiedad Texto que devuelve(get) y establece (set) como valor el texto de ese TextBox. La clase "Y" hace una instancia de la clase "X", por eso se necesita sabeer cuando el valor del textbox ha cambiado y de ahi, la idea de manejar el evento. Ya logre hacerlo por eset camino, pero de cualquier manera me gustaria saber un poco mas acerca de INotifyPropertyChanged, o cual es la mejor forma de obtener ese resultado?. Les agradeceria cualquier comentario, saludos!!!

    martes, 13 de septiembre de 2011 15:04
  • Hola @luigi88,

    Muy buena pregunta :)

    Lo primero es pedir perdón porque hay una linea en mi ejemplo que está en el metodo InicializeComponent que no he mostrado.

    this.textBox1.DataBindings.Add(new System.Windows.Forms.Binding("Text", this.personaBindingSource, "Nombre", true));

    Esta es la que asocia la propiedad Text del TextBox con la propiedad Nombre de Persona.

    Te voy a contestar desde tu última solicitud hasta la primera, es decir en orden inverso.

    1. Si la clase X es un UserControl, creo que estás volviendo a repetir código y te explico porque, no necesitas más que esto.

     

    public class MiTexto:TextBox
        {
            
        }
    

     


    Logicamente en MiTexto tienes que implementar tus funcionalidades y no necesitas ni eventos ni INotifyPropertyChanged.¿Que quieres hacer con la propiedad Texto si ya tienes Text?

    2. La clase Y Tiene que ser o un UserControl,Forms etc, no le veo sentido a la clase X si Y no es esto.

    3. Tu sigues diciendo que quieres Texto y no Text, haces bien en reivindicar nuestro idioma :), pues puedes hacer lo siguiente.

     

    public class MiTexto:TextBox
        {
            [Browsable(false)]
            [Bindable(BindableSupport.No)]
            public override string Text
            {
                get
                {
                    return base.Text;
                }
                set
                {
                    base.Text = value;
                }
            }
            [DefaultValue("")]
            [Bindable(BindableSupport.Yes)]
            public string Texto
            {
                get
                {
                    return base.Text;
                }
                set
                {
                    base.Text = value;
                }
            }
    
        }
    

     


    Ahora puedes hacer binding a Texto y no a Text tal y como te muestro

    Antes

     

    Después

    En otra respuesta te contesto a no vaya a ser que al enviar me diga que se ha producido un error y me ha costado mi trabajo:)

    y el ejemplo que mencionas arriba tengo una pregunta, como se le notifica a la clase Form3 que ha cambiado el valor de lapropiedad Nombre de la clase Persona?

    Saludos,



    phurtado
    martes, 13 de septiembre de 2011 22:38
    Moderador
  • Hola,

    Realmente quien hace todo es BindingSource y Binding. Esos son los responsables de transmitir la información entre Persona y Form3.

    Para explicarlo hace falta un rio de tinta y yo menos William_Shakespeare soy cualquier cosa :).

     

    Pero voy a intertar exponerlo. Cuando haces binding lo normal es que los datos vayan en las dos direcciones, es decir desde la propiedad del  control a la propiedad de la clase y desde la propiedad de la clase a la propiedad a la propiedad del Control.

    En Ambos casos se necesita utilizar System.Reflection la verdad que un gran amigo, recuerdo las criticas de hace años y sobre todo cuando te tomaban por loco hace más CallByName

    1. Desde la propiedad de la clase a la propiedad del Control

    Internamente Binding Y BindingSource hacer algo parecido a esto.

     

    PropertyInfo origen = this._Persona.GetType().GetProperty("Nombre");
    
                if (origen != null)
                {
                    PropertyInfo destino = this.textBox1.GetType().GetProperty("Text");
                    if (destino != null)
                    {
                        object Valor = origen.GetValue(_Persona, null);
                        destino.SetValue(this.textBox1, Valor, null);
                    }
                }
                
    

     


    Fijate que utilizo Text, no reinventes, todos hemos pasado por eso y las consecuencias suelen ser nefastas.

    2.Desde la propiedad del Control a la Propiedad de la Clase.

    Esto es un poco más complicado, pero básicamente esta basado en algo parecido. Es más complicado porque cualquier objeto implementa ToString() y por tanto es facil convertir de cualquier valor a string que en definitiva es lo que ves en la propiedad Text del Control.

    Para realizar el mecanismo Propiedad->Control To Clase->Propiedad tienes que utilizar  TypeConverter. Es logico tienes que convertir, por ejemplo un "1" to int32, que es el tipo que has definido en la clase.

     

    Basicamente este paso esta basado en el control del evento "Validating", "EventDescriptorCollection" y "EventDescriptor"

     

    Paso el código interno de Binding para que veas lo que te comento.

     

     

    private void CheckBinding()
    {
        this.bindToObject.CheckBinding();
        if ((this.control == null) || (this.propertyName.Length <= 0))
        {
            this.propInfo = null;
            this.validateInfo = null;
        }
        else
        {
            PropertyDescriptorCollection properties;
            this.control.DataBindings.CheckDuplicates(this);
            Type componentType = this.control.GetType();
            string b = this.propertyName + "IsNull";
            PropertyDescriptor descriptor = null;
            PropertyDescriptor descriptor2 = null;
            InheritanceAttribute attribute = (InheritanceAttribute) TypeDescriptor.GetAttributes(this.control)[typeof(InheritanceAttribute)];
            if ((attribute != null) && (attribute.InheritanceLevel != InheritanceLevel.NotInherited))
            {
                properties = TypeDescriptor.GetProperties(componentType);
            }
            else
            {
                properties = TypeDescriptor.GetProperties(this.control);
            }
            for (int i = 0; i < properties.Count; i++)
            {
                if ((descriptor == null) && string.Equals(properties[i].Name, this.propertyName, StringComparison.OrdinalIgnoreCase))
                {
                    descriptor = properties[i];
                    if (descriptor2 != null)
                    {
                        break;
                    }
                }
                if ((descriptor2 == null) && string.Equals(properties[i].Name, b, StringComparison.OrdinalIgnoreCase))
                {
                    descriptor2 = properties[i];
                    if (descriptor != null)
                    {
                        break;
                    }
                }
            }
            if (descriptor == null)
            {
                throw new ArgumentException(SR.GetString("ListBindingBindProperty", new object[] { this.propertyName }), "PropertyName");
            }
            if (descriptor.IsReadOnly && (this.controlUpdateMode != ControlUpdateMode.Never))
            {
                throw new ArgumentException(SR.GetString("ListBindingBindPropertyReadOnly", new object[] { this.propertyName }), "PropertyName");
            }
            this.propInfo = descriptor;
            Type propertyType = this.propInfo.PropertyType;
            this.propInfoConverter = this.propInfo.Converter;
            if (((descriptor2 != null) && (descriptor2.PropertyType == typeof(bool))) && !descriptor2.IsReadOnly)
            {
                this.propIsNullInfo = descriptor2;
            }
            EventDescriptor descriptor3 = null;
            string str2 = "Validating";
            EventDescriptorCollection events = TypeDescriptor.GetEvents(this.control);
            for (int j = 0; j < events.Count; j++)
            {
                if ((descriptor3 == null) && string.Equals(events[j].Name, str2, StringComparison.OrdinalIgnoreCase))
                {
                    descriptor3 = events[j];
                    break;
                }
            }
            this.validateInfo = descriptor3;
        }
        this.UpdateIsBinding();
    }
    
     
    
     
    
    

    Saludos,

     

     

     


    phurtado
    martes, 13 de septiembre de 2011 23:06
    Moderador
  • Hola Pedro Hurtado

    Muchas gracias por tus comentarios, fueron de gran utilidad, saludos!!!

    martes, 20 de septiembre de 2011 14:45