Una vez que podemos pasar datos entre formularios como en la entrada Mensajería únicaWPF 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 Bindng
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 claseMessage
que copia las propiedades de un objetoMessage
al mismo objeto. - Implementación de la interfaz
INotifyPropertyChanged
por la claseMessage
de modo que debe implementar el métodoOnPropertyChanged
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 eventothis.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 { _progress = value; OnPropertyChanged("progress"); } } public string infoText { get { return _infotext; } set { _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 /// <summary> /// Asigna los valores al mensaje /// </summary> /// <param name="_text">Texto del mensaje /// <param name="_isVisible">Visibilidad del mensaje /// <param name="_progress">Progreso public void setMessage(string _text, bool _isVisible, int _progress) { this.infoText = _text; this.progressIsVisible = _isVisible; this.progress = _progress; } /// <summary> /// Borra los valores del mensaje /// </summary> public void Clear() { setMessage("", false, 0); } /// <summary> /// Efectua una copia de las propiedades de un mensaje /// </summary> /// <param name="message">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