Muy buenas!!!
antes de nada quería decir que tengo poquita experiencia y estoy aprendiendo, por lo que si la respuesta es algo básico perdonad mi "ignorancia" y os pido un poco de paciencia.
Os expongo a continuación lo que me gustaría conseguir no sé si de la mejor manera, o por lo menos la más elegante, con eventos.
Tengo las siguientes clases:
Public Class Principal
Public objeto1 As ClaseIncluida1
Public objeto2 As ClaseIncluida2
Public total As Integer
End Class
Public Class ClaseIncluida1
Public atributoC11 As Integer
Public atributoC12 As Integer
End Class
Public Class ClaseIncluida2
Public atributoC21 As Integer
Public atributoC22 As Integer
End Class
Me gustaría definir el comportamiento de la clase Principal para que cada vez que se asignase un valor a ClaseIncluida1.atributoC11 se actualizase con el mismo valor ClaseIncluida2.atributoC21 y a continuación se actualizase el valor de Principal.total con el valor de la suma de los atributos de las clases que contiene.
Sé que hay soluciones fáciles pero me gustaría que la clase Principal hiciera todo eso de forma automática, por lo que mi sentido común me dice que es lo que hace un evento.
He leido sobre el patrón observador y que en .NET se implementa mediante el uso de eventos y delegados. Conozco y entiendo esos conceptos y los he utilizado en otras ocasiones, pero no sé utilizarlos en este caso que he planteado.
He pensado que quizás el uso de eventos no sería una solución viable.
Si alguien pudiera darme alguna directriz para poder solucionar el problema se lo agradecería mucho :)
Puesto que dices que estas aprendiendo intentaré ser muy descriptivo:
Yo creo que más que un evento lo que necesitas usar es una propiedad. Una propiedad se usa para modificar y leer el valor de un componente de una clase a través de un interface similar a una lectura o escritura normal. Es decir, si tienes un valor string llamado var1 dentro de clase1 puedes dejarlo como privado (invisible desde fuera) y definir la propiedad var1, crearás el procedimiento set() y la función get() que se corresponden a lo ejecutado cuando se quiere leer o escribir el valor de la propiedad, y ahí puedes introducir todo el código adicional que necesites, si quieres lanzas un evento o modificas el valor directamente. Al principio parecerá algo complicado, pero verás que es una idea muy sencilla, te pongo un ejemplo.
Vamos a crear una clase con un sólo hijo de tipo integer, es la que llevará la propiedad:
* definimos el hijo como miembro privado para ocultarlo (puesto que accederemos a través de la propiedad)
Public Class testclass1
Private my_number As Integer = 0
End Class
Y ahora queremos definir el cómo se lee o escribe my_number así que definimos la propiedad:
* get() y set() deben de tener siempre el mismo nombre, si usas ms visual studio verás que el ide lo escribe automático en cuanto pones get, así que no te preocupes.
* la propiedad tiene tipo así que en este caso usaremos el mismo del valor que queremos modificar, osea integer.
Public Class testclass1
Private my_number As Integer = 0
Public Property number As Integer
Get
'// aquí va el código que se ejecutará al leer,
'// es como una función, necesita return
End Get
Set(ByVal value As Integer)
'// aquí va el código que se ejecutará al escribir
'// es donde quieres ejecutar el código adicional
End Set
End Property
End Class
Bien, ahora vamos a hacer que al leer tan sólo se devuelva el valor de my_number y al escribir lo haga y además lance un evento llamado need_update y que devuelva el valor.
Nos queda así:
Public Class testclass1
Private my_number As Integer = 0
' Defino el evento de testclass1, recuerda que al crear la instancia debes hacerlo usando "withevents"
Public Event need_update(ByVal valor_actualizado As Integer)
Public Property number As Integer
Get
Return Me.my_number
End Get
Set(ByVal value As Integer)
Me.my_number = value
RaiseEvent need_update(my_number)
End Set
End Property
End Class
Recuerda que cuando creas el objeto debes hacerlo así, usando withevents puesto que este objeto contiene un evento que queremos usar:
Citarpublic withevents nuevotest as new testclass1
A mi personalmente me gusta más no usar eventos puesto que si windows está muy ocupado con muchas tareas va apilando los eventos en una cola y se ejecutan a trompicones. Puedes, si tienes bien localizado el objeto a modificar hacer automáticamente la modificación, es decir, si queremos modificar una clase igual que tu ClaseIncluida2 que se llama "Incluida" y pertenece a form1:
Public Class testclass1
Private my_number As Integer = 0
Public Property number As Integer
Get
Return Me.my_number
End Get
Set(ByVal value As Integer)
Me.my_number = value
form1.Incluida.atributoC21=value
End Set
End Property
End Class
Espero haberte aclarado algunas cosas y espero que sea lo que buscas, si tienes dudas no te cortes en preguntar.
Hola ABDERRAMAH, muchas gracias por tu repuesta :)
No he utilizado propiedades para minimizar el código del ejemplo que he puesto, pero su implementación es una de las que considero buenas prácticas que intento recordar, pero de todas formas mcuhas gracias por tu detallada explicación :)
No entiendo muy bien la asignación que realizas en el último código que has escrito:
Public Class testclass1
Private my_number As Integer = 0
Public Property number As Integer
Get
Return Me.my_number
End Get
Set(ByVal value As Integer)
Me.my_number = value
form1.Incluida.atributoC21=value
End Set
End Property
End Class
En especial esto:
form1.Incluida.atributoC21=value
Si es de la manera que indicas, ¿entonces mis clases quedarían así?
Public Class Principal
Public objeto1 As ClaseIncluida1
Public objeto2 As ClaseIncluida2
Public total As Integer
End Class
Public Class ClaseIncluida1
Private _atributoC11 As Integer
Public _ atributoC12 As Integer
Public Property atributoC11 As Integer
Get
Return _atributoC11
End Get
Set(ByVal value As Integer)
_atributoC11 = value
Principal.objeto2._atributoC21 = value
End Set
End Property
End Class
Public Class ClaseIncluida2
Public _atributoC21 As Integer
Public _atributoC22 As Integer
End Class
El error que me indica Visual Studio es La referencia a un miembro no compartido requiere una referencia de objeto.
Creo que es algo similar a lo que me salía intentado hacerlo con eventos :-(
El problema es que no accedes desde el principio de la estructura. Fíjate que yo no hago:
CitarIncluida.atributoC21=value
sino que accedo desde form1
CitarForm1.Incluida.atributoC21=value
form1 es mi formulario principal, de esta forma incluso puedes modificar cosas en otros formularios hijos (al fin y al cabo un formulario es un objeto) por ejemplo, desde el formulario 1 creo dos formularios más:
Citar
public x as new form2
public y as new form3
ahora, en el código de form3 puedo hacer:
Citarform1.x.title="funcionó"
y así modifico desde y (form3) un valor de x (form2) pero fijate que tengo que acceder desde form1 puesto que es quien creó todo lo demás.
Muy buenas de nuevo ABDERRAMAH!!! :)
Creo que más que acceder desde el principio de la estructura es acceder desde una instancia de la clase Principal, que es lo que me indicas tú al realizar ese acceso desde un formulario, dado que la instancia de la clase que llamas Incluida se encuentra en dicho formulario.
Yo quería que la clase Principal hiciera automáticamente esos comportamientos sin tener que realizarlos desde clases externas como formularios en los que se instanciará dicha clase Principal.
Había pensado que mediante delegados y eventos podría hacerlo, pero me encuentro con problemas similares al intentar acceder a una clase desde otra estando las dos contenidas en una.
Esto parece un poco lío, pero creo que con el código de mi primer mensaje está claro... o eso creo :S
Mediante delegados y eventos ¿¿podría realizar lo que quiero??
Insisto en usar delegados y eventos dado que me parece importante aprender su uso.