miércoles, 13 de julio de 2016

Mensajería única (2)

Una vez que podemos pasar datos entre formularios como en la entrada Mensajería única, WPF nos lo pone mucho más fácil y con mucho menos código.
Lo primero de todo debemos enlazar los controles con los datos de la clase y para ello debemos tener algún conocimiento de XAML y WPF (no es el fin de este artículo enseñar WPF)

Para llevar a cabo el paso de mensajes entre formularios mediante XAML, he creado una nueva ventana con un control Grid con tres columnas y tres filas, un control TextBlock y un control ProgressBar.
Las propiedades de los controles se enlazan con datos, estos datos proceden de una clase de negocio y esta clase es Message.
En WPF, para instanciar una clase, debemos hacerlo desde los recursos de la página o de un control, dependiendo del ámbito que se pretenda abarcar. En este caso, yo declaro la clase Message en los recursos del objeto Grid.

Una vez declarada, se creará una instancia de Message en cada nueva ventana y los controles se enlazan a la clase con Binding en la propiedad del control que pretendemos enlazar y haciendo referencia a la propiedad de la clase Message. He incluido un conversor en la propiedad Visibility del control ProgressBar para convertir valores booleanos en valores de la enumeración Visibility como Visible, Hidden o Collapse; si es true, Visible y si es False, Collapse. En este caso hacemos los mismo, que es declarar la clase ConvertBoolToVisible (la cual implementa la interfaz IValueConverter) con una key llamada btv.
He hecho un binding sobre la propiedad Text del TextBlock a la propiedad infoText de la clase Message, la propiedad Value del control ProgressBar con la propiedad progress de Message y por último la propiedad Visibility con progressIsVisible ya su vez con el conversor btv.

<Window x:Class="WpfApplication1.Window2"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:WpfApplication1"
        mc:Ignorable="d"
        Title="Window2" Height="300" Width="300">
    
    <Grid x:Name="grid" DataContext="message">
        <Grid.Resources>
            <local:ConvertBoolToVisible x:Key="btv"/>
            <local:Message x:Key="message"/>

        </Grid.Resources>
        <Grid.RowDefinitions>
            <RowDefinition Height="100"/>
            <RowDefinition Height="100*"/>
            <RowDefinition Height="100*"/>
        </Grid.RowDefinitions>

        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="100*"/>
            <ColumnDefinition Width="100*"/>
            <ColumnDefinition Width="100*"/>
        </Grid.ColumnDefinitions>
        <TextBlock x:Name="tb" Text="{Binding infoText}" Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="3" HorizontalAlignment="Center" VerticalAlignment="Center"/>
        <ProgressBar x:Name="pb" Minimum="0" Maximum="100" Value="{Binding progress}" 
                     Visibility="{Binding Path=progressIsVisible, Converter={StaticResource btv}}"
                     Grid.Column="0" Grid.Row="1" Grid.ColumnSpan="3" HorizontalAlignment="Center" Width="150" Margin="71,0,71,65"/>
    </Grid>
</Window>

Y el código (code-behind)? Muchísmo más reducido, ya que en este caso solo existe un objeto Message de clase, asignamos dinámicamente el DataContext del control Grid (que será desde donde se alimenten los controles) y añadimos el evento CollectionChanged.
Para este caso, he efectuado dos cambios en la clase Message:
  • Creación de un nuevo método público llamado Clone en la clase Message que copia las propiedades de un objeto Message al mismo objeto.
  • Implementación de la interfaz INotifyPropertyChanged por la clase Message de modo que debe implementar el método OnPropertyChanged para que la ventana detecte los cambios en las propiedades y pueda asignar los valores.
  • Creación del método OnPropertyChanged("propertyName") el cual llamará al evento this.PropertyChanged(this, new PropertyChangedEventArgs(p_PropertyName));
  • Modificación en el set de las propiedades, incluyendo la llamada al método OnPropertyChanged("propertyName").
Cada vez que se añada un nuevo mensaje, se clonará el objeto mensaje con los nuevos datos y se visualizarán en pantalla efectuando el mismo resultado que conseguimos mediante código, pero esta vez con menos código.

Clase Message modificada
using System;
using System.ComponentModel;

namespace WpfApplication1
{
    public class Message: INotifyPropertyChanged
    {
        #region Constructor

        public Message()
        {
            this.infoText = "";
            this.progress = 0;
            this.progressIsVisible = false;
        }

        public Message(string _text, bool _isVisible, int _progress)
        {
            setMessage(_text, _isVisible, _progress);
        }

        #endregion

        #region Properties

        private string _infotext;
        private int _progress;

        public int progress
        {
            get { return _progress; }
            set
            {
                //if (_progress!=value)
                //{

                //}

                _progress = value;

                OnPropertyChanged("progress");
            }
        }

        public string infoText
        {
            get { return _infotext; }
            set
            {
                //if (_infotext!=value)
                //{
                //    EventArgsMessage eventArgsMessage = new EventArgsMessage(_infotext,value);
                //}

                _infotext = value;
                OnPropertyChanged("infoText");
            }
        }

        private bool _progressIsVisible;

        public bool progressIsVisible
        {
            get { return _progressIsVisible; }
            set
            {
                _progressIsVisible = value;
                OnPropertyChanged("progressIsVisible");
            }
        }

        public string infoText2 { get; set; }

        #endregion

        #region Events

        public event EventHandler<EventArgsMessage> ChangedTextEventHandler;
        public event EventHandler<EventArgsProgress> ChangedProgressEventHandler;
        public event PropertyChangedEventHandler PropertyChanged;

        public void OnPropertyChanged(String p_PropertyName)
        {
            if (this.PropertyChanged != null)
            {
                this.PropertyChanged(this, new PropertyChangedEventArgs(p_PropertyName));
            }
        }

        #endregion

        #region Private Methods

        void OnChangedText(EventArgsMessage e)
        {
            if (ChangedTextEventHandler!=null)
            {
                ChangedTextEventHandler(this, e);
            }
        }

        void OnChangedProgress(EventArgsProgress e)
        {
            if (ChangedProgressEventHandler!=null)
            {
                ChangedProgressEventHandler(this, e);
            }
        }

        #endregion

        #region Public Methods

        /// 
        /// Asigna los valores al mensaje
        /// 
        /// Texto del mensaje
        /// Visibilidad del mensaje
        /// Progreso
        public void setMessage(string _text, bool _isVisible, int _progress)
        {
            this.infoText = _text;
            this.progressIsVisible = _isVisible;
            this.progress = _progress;
        }

        /// 
        /// Borra los valores del mensaje
        /// 
        public void Clear()
        {
            setMessage("", false, 0);
        }

        /// 
        /// Efectua una copia de las propiedades de un mensaje
        /// 
        /// Objeto a clonar
        public void Clone(Message message)
        {
            this.infoText = message.infoText;
            this.infoText2 = message.infoText2;
            this.progressIsVisible = message.progressIsVisible;
            this.progress = message.progress;
        }

        #endregion
    }

    public class EventArgsMessage:EventArgs
    {
        public EventArgsMessage() { }
        public EventArgsMessage (string _oldText, string _newText)
        {
            this.oldText = oldText;
            this.newText = _newText;
        }

        public string oldText { get; set; }
        public string newText { get; set; }
    }

    public class EventArgsProgress : EventArgs
    {
        public EventArgsProgress() { }
        public EventArgsProgress(int _newValue)
        {
            this.newValue = _newValue;
        }

        public int newValue { get; set; }
    }

}

Code-Behind de la ventana

public partial class Window2 : Window
    {
        public Window2()
        {
            InitializeComponent();
            App.messages.CollectionChanged += Messages_CollectionChanged;
            this.grid.DataContext = m;
        }

        Message m = new Message();

        private void Messages_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
        {
            if (e.Action == System.Collections.Specialized.NotifyCollectionChangedAction.Add)
            {
                foreach (var item in e.NewItems)
                {
                    m.Clone((Message)item);
                }
            }
            
        }
    }

He aquí una muestra


Pues esto es todo. Estoy a vuestra disposición para cualquier duda.

Un saludo

No hay comentarios:

Publicar un comentario