Colabora
 

Actualización simultánea de varias ventanas

Técnica para actualizar simultaneamente varias ventas de manera autómatica utlizando los eventos.

Fecha: 18/Dic/2007 (18-12-07)
Autor: Kalay (Jordi Masriera) Kalay_jy_99 EN hotmail.com (EN es @)

Introducción

Todo empezó cuando me pidieron que hiciera un programa que visualmente se pareciera al explorador de Windows. En el explorador puedes ver el mismo objeto tanto en árbol de la izquierda como en la lista del centro. Si cambias el nombre en uno de ellos automáticamente se tiene que cambiar en el otro. Además puedes tener varios exploradores abiertos y todos deben refrescarse automáticamente. Lo que intento explicar es una manera para hacer esto.

Para ver mejor lo que digo quizás mejor si os bajáis el ejecutable y lo probáis vosotros mismos. Al ejecutar el programa se abrirá la siguiente pantalla:

 

Cada vez que apretes el botón aparecerá una nueva pantalla como esta:

 

Abre unas cuantas pantallas del árbol  y prueba de cambiar el texto de un nodo (seleccionar un nodo a apretar el botón "Edit"), eliminarlo (seleccionar un nodo y apretar el botón "Delete") o crear un hijo(seleccionar un nodo y apretar el botón "Add") y verás como en todas las ventanas que tengas abiertas sucede lo mismo.

Estructura

Para hacer esto tenemos dos capas: la capa lógica, que contiene los objetos con los datos, y la capa visual, que contiene los nodos visuales. La idea es que al actualiza la capa lógica desde cualquier punto del programa se lance un evento que recibe la capa visual y actualiza los datos que se muestran por pantalla. Al añadir un hijo en la lista o al borrar un nodo también se lanza un evento que actualiza todos los árboles a la vez. De esta manera no hace falta tener una lista de pantallas donde se está visualizando el nodo.

El código:

En la capa lógica tenemos dos clases, la case MyDatatClass que representa un nodo del árbol lógico. Esta clase contiene una lista de hijos:

/// <summary>
/// Evento que lanza el objeto.
/// </summary>
public delegate void DataEvent();

/// <summary>
/// Clase que contiene los datos, en este caso sólo contiene un string 
/// y una lista de hijos.
/// </summary>
public class MyDatatClass {
    /// <summary>
    /// Evento de modificación del texto del objeto
    /// </summary>
    private DataEvent modifyEvent;
    /// <summary>
    /// Evento de borrado del objeto
    /// </summary>
    private DataEvent deleteEvent;

    /// <summary>
    /// Clase padre en el arbol
    /// </summary>
    private MyDatatClass parent;
    /// <summary>
    /// Texto del objeto
    /// </summary>
    private string text;
    /// <summary>
    /// Lista de hijos de objeto
    /// </summary>
    private DataList list;
    /// <summary>
    /// Creador de la clase
    /// </summary>
    /// <param name=parent">nodo
        padre en la estructura de arbol</param>"
    /// <param name=text">texto
        del nodo</param>"
    public MyDatatClass(MyDatatClass parent, string text) {
    this.text = text;
    this.list = new DataList();
    this.parent = parent;
    }
    /// <summary>
    /// Obtiene o establece el texto del objeto
    /// </summary>
    public string Text {
    get {
        return text;
        }
        set {
            text = value;
            if (modifyEvent != null)
            modifyEvent();
            }
            }
            /// <summary>
            /// Borra el objeto.
            /// </summary>
            public void Delete() {
            if (parent != null)
            parent.List.Remove(this);

            if (deleteEvent != null)
            deleteEvent();
            }
            /// <summary>
            /// Obtiene la lista de hijos del objeto
            /// </summary>
            public DataList List {
            get {
                return list;
                }
                }
                /// <summary>
                /// Obtiene o establece el evento de modificación del
                /// texto del objeto.
                /// </summary>
                public DataEvent ModifyEvent {
                get {
                    return modifyEvent;
                    }
                    set {
                        modifyEvent = value;
                        }
                        }
                        /// <summary>
                        /// Obtiene o establece el evento de borrado del objeto.
                        /// </summary>
                        public DataEvent DeleteEvent {
                        get {
                            return deleteEvent;
                            }
                            set {
                                deleteEvent = value;
                                }
                                }
                                }


También tenemos la clase DataList que simplemente es una lista que lanza un evento cuando se le añade un nodo.

En la capa visual tenemos MyDataTreeNode. Es una clase que hereda de TreeNode para poderlo poner dentro de un TreeView. Esta es la clase que recibe los eventos lanzados por MyDataClass y DataList cuando modificamos el objeto.

/// <summary>
/// Esta clase hereda de TreeNode.
/// Es un TreeNode que contiene un objeto MyDatatClass.
/// El texto del TreeNode debe ser el texto del objeto MyDatatClass.
/// </summary>
class MyDataTreeNode : TreeNode {
    /// <summary>
    /// Objeto MyDatatClass que representa el nodo
    /// </summary>
    private MyDatatClass mydata;
    /// <summary>
    /// Creador de MyDataTreeNode.
    /// </summary>
    /// <param name=mydata">objeto que debe representar en el arbol</param>"
    public MyDataTreeNode(MyDatatClass mydata) {
    this.mydata = mydata;
    base.Text = mydata.Text;
    // Vinculamos el evento Modify con la función MyDataClass_Modify
    mydata.ModifyEvent += new DataEvent(MyDataClass_Modify);
    // Vinculamos el evento Modify con la función MyDataClass_Modify
    mydata.DeleteEvent += new DataEvent(MyDataClass_Delete);
    // Vinculamos el evento Add de la lista con la función MyDataList_Add
    mydata.List.AddEvent += new ListEvent(MyDataList_Add);
    }
    /// <summary>
    /// Destructor de MyDataTreeNode
    /// </summary>
    public void Dispose() {
    mydata.ModifyEvent -= new DataEvent(MyDataClass_Modify);
    mydata.DeleteEvent -= new DataEvent(MyDataClass_Delete);
    mydata.List.AddEvent -= new ListEvent(MyDataList_Add);
    }
    /// <summary>
    /// Obtiene el objeto MyDatatClass que representa el nodo
    /// </summary>
    public MyDatatClass MyData {
    get {
        return mydata;
        }
        }
        /// <summary>
        /// Evento de modificación del texto del objeto mydata.
        /// Este evento lo lanza mydata cuando le cambian el texto,
        /// el nodo recibe el evento y actualiza el texto del TreeNode.
        /// </summary>
        private void MyDataClass_Modify() {
        base.Text = mydata.Text;
        }
        /// <summary>
        /// Evento de borrado del objeto mydata.
        /// Este evento lo lanza mydata cuando se borra el objeto,
        /// el nodo recibe el evento y se borra de la lista.
        /// </summary>
        private void MyDataClass_Delete() {
        this.Dispose();
        Parent.Nodes.Remove(this); ;
        }
        /// <summary>
        /// Evento de añadido de un nuevo objeto hijo del actual.
        /// Este evento lo lanza mydata cuando se le añade un hijo,
        /// el nodo recibe el evento y añade el hijo a la lista.
        /// </summary>
        private void MyDataList_Add(MyDatatClass mydata) {
        base.Nodes.Add(new MyDataTreeNode(mydata));
        }
        }

En este ejemplo sólo está hecho con árboles pero en la aplicación real tengo todo tipo de elementos que se actualizan automáticamente como por ejemplo listviews. Para aplicarlo en un listview lo único que tenemos que hacer es hacer una clase que herede de ListViewItem parecida a MyDataTreeNode. Esto permite no tenerte que preocupar de actualizar todas las ventanas al cambiar un valor. Con una pequeña modificación el la recepción de los eventos podríamos hacerlo multi threading de manera que tengamos un thread que consulta el estado de un aparato y cambia el estado en la capa lógica. Esto hace que se lance el evento y se actualice la vista sin tener ningún timer ni ningún pooling consultando el estado.

Este ejemplo ha sido creado en Visual Studio 2005 y en el lenguaje C#.


Espacios de nombres usados en el código de este artículo:

System.Windows.Forms

 


Código de ejemplo (comprimido):

 

Fichero con el código de ejemplo: kalay_ActualizadorPorEventosSRC.zip - 20.4 KB

(MD5 checksum: 6114F1E7BBB56F852978FF78B7EA03DC)

 

Fichero con el ejecutable: kalay_ActualizadorPorEventosEXE.zip - 6.78 KB

(MD5 checksum: F355D261F40E29B47C692FDAC1027ED4)

 


Ir al índice principal de el Guille