.NET Chronicles

Temas relacionados con el desarrollo de aplicaciones con .NET

Octavio Telis

Uso de la propiedad Visibility con valores booleanos en WPF

   

Hola que tal.

En esta ocasión quiero compartir con ustedes una manera para utilizar la propiedad Visibility con un valor bool. Te preguntarás “¿Y qué sentido tiene eso?”, bien, el detalles está en que en ocasiones se requiere que la visibilidad de un control de la interfaz de usuario responda a un valor de true o false.

En Windows Forms, la propiedad Visibility no existe en los controles de interfaz de usuario, sin embargo, existe la propiedad Visible que es del tipo bool. En WPF la propiedad Visibility es del tipo System.Windows.Visibility que es un enumerados con tres miembros a saber; Visible, Hidden y Collapsed. Visible establece que el control estará visible, Hidden establece que el control estará oculto y no visible, pero, conservando la relación de aspecto de donde se encuentra dibujado el control, esto es, que el espacio donde está el control permanece intacto, como si estuviera el control ahí, ocupando su espacio, pero sin mostrarse y Hidden, que por el contrario, oculta el control pero permite que el espacio que ocupa el control pueda ser contraído, liberando espacio visual, esto se puede ver más claramente con controles contenedores como el control StackPanel. En WPF, si quieres ocultar un control en base a un valor booleano no es posible de forma directa, por lo que la asignación de un valor booleano, procedente de una clase de datos o de otro control no es posible sin el uso de una estructura de decisión, por ejemplo, no se puede asignar directamente la propiedad IsChecked de un CheckBox a la propiedad Visibility de un control, para ocultarlo o mostrarlo según sea el valor de la propiedad IsChecked; true o false. Comúnmente, si quisiera mostrar el control desearía pasar un valor true y que se mostrara y si quisiera ocultarlo pasara un valor false y se ocultara, como es el caso de los controles  de Windows Forms.

Para ejemplificar esta situación y cómo lo resolveríamos, vamos a utilizar el ejemplo de la publicación anterior (http://bit.ly/Lu8Eft) pero modificando un poco la interfaz de usuario, la cual quedaría de la siguiente manera:

<Window x:Class=”EjemplosWPF.MainWindow”

        xmlns=”http://schemas.microsoft.com/winfx/2006/xaml/presentation”

        xmlns:x=”http://schemas.microsoft.com/winfx/2006/xaml”

        xmlns:conv =”clr-namespace:EjemplosWPF”

        Title=”MainWindow” Height=”350″ Width=”525″>

    <Window.Resources>

        <conv:ConvertByteArrayToBitmapImage x:Key=”ByteArrayToBitmapImage”/>

    </Window.Resources>

    <Grid>

        <StackPanel HorizontalAlignment=”Center” VerticalAlignment=”Center”>

            <CheckBox x:Name=”chkMostrarImagen” Content=”Mostrar Imagen”

                      Margin=”5″ Checked=”chkMostrarImagen_Checked”

                      Unchecked=”chkMostrarImagen_Checked

                      IsChecked=”True”/>

            <Image x:Name=”imgFoto” Width=”100″ Height=”100″ Margin=”5″

                   Source=”{Binding Image, UpdateSourceTrigger=PropertyChanged,

                            Mode=TwoWay,

                            Converter={StaticResource ByteArrayToBitmapImage}}”/>

            <Button x:Name=”btnFoto” Width=”100″ Height=”25″

                    Content=”Agregar Foto” Margin=”5″ Click=”btnFoto_Click”/>

        </StackPanel>

    </Grid>

</Window>

 

Lo único que cambió es que agregué un control CheckBox denominado chkMostrarImagen con la propiedad IsChecked asignada a True. La funcionalidad que se busca debe hacerse utilizando el evento Checked del control chkMostrarImagen, que se lanza cuando la casilla se ha marcado y por otra parte, en el evento Unchecked que se lanza cuando la casilla se ha desmarcado, ambos apuntan al mismo controlador de evento, para no duplicar el esfuerzo, ya que la acción de marcar y desmarcar realizan la misma tarea que es evaluar el valor de la propiedad IsChecked del control CheckBox y de acuerdo al valor de esta propiedad, mostrar u ocultar el control imgFoto. De antemano, podemos percatarnos de que implementar la funcionalidad que se requiere implica escribir código adicional para la interfaz de usuario. El código del controlador de evento para Checked y Unchecked quedaría como sigue:

private void chkMostrarImagen_Checked(object sender, RoutedEventArgs e)

{

    if (imgFoto == null)

        return;

    if (chkMostrarImagen.IsChecked.Value)

        imgFoto.Visibility = System.Windows.Visibility.Visible;

    else

        imgFoto.Visibility = System.Windows.Visibility.Collapsed;

}

 

Hay que comentar algo importante al respecto de este código, al inicio he tenido que agregar una validación para verificar que el control imgFoto no es null, ya que por el árbol visual de elementos de la interfaz de usuario, al inicio de la ejecución, el primero que se crea es el control chkMostrarImagen y al crearse se le asigna la propiedad IsChecked con true porque así lo declaramos inicialmente lo que provoca que se lance el evento Checked cuando aun no se ha creado el control imgFoto. Podríamos evitar esta validación si nosotros enlazamos el evento posteriormente a que se hayan creado todos los elementos de la interfaz de usuario, lo cual es posible realizar en el constructor de la ventana, inmediatamente después del método InitializeComponent, para esto, debemos eliminar la declaración de los eventos Checked y Uncheked de la declaración del control CheckBox. ¿Que cómo sería?, bueno, pues veamos:

La declaración del CheckBox quedaría así:

<CheckBox x:Name=”chkMostrarImagen” Content=”Mostrar Imagen”

            Margin=”5″ IsChecked=”True”/>

 

El constructor en la clase de la ventana quedaría así:

public MainWindow()

{

    InitializeComponent();

    chkMostrarImagen.Checked +=

        new RoutedEventHandler(chkMostrarImagen_Checked);

    chkMostrarImagen.Unchecked +=

        new RoutedEventHandler(chkMostrarImagen_Checked);

    foto = new Foto();

    DataContext = foto;

}

 

El manejador del evento quedaría ahora así:

private void chkMostrarImagen_Checked(object sender, RoutedEventArgs e)

{

    if (chkMostrarImagen.IsChecked.Value)

        imgFoto.Visibility = System.Windows.Visibility.Visible;

    else

        imgFoto.Visibility = System.Windows.Visibility.Collapsed;

}

 

En fin, hemos tenido que escribir más código aún, pero funciona. Aunque, ¿no sería mejor que el propio control pudiera hacer todo esto y que yo no escribiera más código que el necesario? La respuesta por supuesto que debe ser afirmativa, pues es parte de lo que trata este artículo, sin embargo, ¿cómo podríamos hacer esto posible?, bien, la factibilidad recae en usar la clase Binding y crear una clase de conversión (conocidas como Converters) implementando la interfaz IValueConverter. Yo sé que al final dirán, “¿no nos íbamos a librar de escribir el código’”, pues no, pero es verdad que ganamos código que no volveremos a escribir mas que una vez y para siempre, para la siguiente vez que lo utilicen no volverán a escribirlo.

Empecemos por crear el converter, que quedará como lo siguiente:

[ValueConversion(typeof(bool), typeof(Visibility))]

public class ConvertBooleanToVisibilityCollapsed : IValueConverter

{

    public object Convert(object value, Type targetType,

        object parameter, CultureInfo culture)

    {

        bool? res = value as bool?;

        if (res != null && res.HasValue)

        {

            if (res.Value)

                return Visibility.Visible;

            else

                return Visibility.Collapsed;

        }

        return value;

    }

    public object ConvertBack(object value, Type targetType,

        object parameter, CultureInfo culture)

    {

        Visibility res = (Visibility value;

        return res == Visibility.Visible ? true : false;

    }

}

 

La mecánica para la definición de una clase converter se encuentra explicada con más detalle con el ejemplo de la publicación anterior (http://bit.ly/Lu8Eft).  El código es similar a lo que hacemos en el método controlador del evento Checked que mostré más arriba, solo que esta vez lo hacemos sobre el valor de origen dentro del método Convert. Ya que el valor de la propiedad IsChecked del control CheckBox es del tipo bool anulable, entonces realizamos la conversión y evaluamos como lo hicimos en el método controlador del evento Checked antes mencionado. Para el valor de vuelta, realizaremos la conversión inversa, vemos que si el valor de la propiedad es Visibility.Visible regresa true y si es lo contrario es false.

Para efectos del ejemplo, declaramos la clase del converter en el mismo archivo de clase de la ventana dentro del namespace del ejemplo.

Ya que tengo mi clase converter, procedemos a las modificaciones pertinentes.

Primero, dejemos el constructor como estaba originalmente, esto es, sin los enlaces del controlador de eventos, quedando así:

public MainWindow()

{

    InitializeComponent();

    foto = new Foto();

    DataContext = foto;

}

 

Seguido de esto, pues… quitemos la declaración del método manejador, denominado chkMostrarImagen_Checked, ya no lo necesitamos más asi que bye.

Tercero, pues la mágia, debemos incluir la declaración del recurso vinculado a nuestra clase recién creada, quedando el bloque de recursos de la ventana de esta manera:

<Window.Resources>

    <conv:ConvertByteArrayToBitmapImage x:Key=”ByteArrayToBitmapImage”/>

    <conv:ConvertBooleanToVisibilityCollapsed x:Key=”BooleanToVisibilityCollapsed”/>

</Window.Resources>

 

El nombre que utilicé, es uno alusivo a la acción que realiza el converter. Ahora tenemos dos, como se puede observar.

Por último, modifiquemos el código del control imgFoto, para que interactúe con el CheckBox. Debemos declarar un nuevo binding, pero con una particularidad más, utilizaremos la propiedad ElementName. Como explicaba en una publicación anterior al hablar del enlace a datos (http://bit.ly/JmGKq8), se pueden enlazar las propiedades entre controles, en este caso, enlazaremos la propiedad IsChecked del control chkMostrarImagen con la propiedad Visibility del control imgFoto. El control chkMostrarImagen será el objeto origen del enlace y el control imgFoto será el objeto destino del enlace. Bien teniendo en mente esta consideración, debemos indicar al objeto Binding del enlace que se está enlazando la propiedad de destino de enlace con una propiedad de origen de enlace de un elemento de la interfaz de usuario, por esta razón incluiremos la propiedad ElementName a la que se le asigna el nombre del control que será el objeto de origen del enlace. Después de este preámbulo, veamos como queda la declaración:

<Image x:Name=”imgFoto” Width=”100″ Height=”100″ Margin=”5″

        Source=”{Binding Image, UpdateSourceTrigger=PropertyChanged,

                Mode=TwoWay,

                Converter={StaticResource ByteArrayToBitmapImage}}”

        Visibility=”{Binding IsChecked, ElementName=chkMostrarImagen,

                Converter={StaticResource BooleanToVisibilityCollapsed}}”/>

 

Básicamente, enlazar las propiedades entre controles es muy similar a la manera en que se hace con los objetos propios, la particularidad es el uso de la propiedad ElementName. Cabe mencionar que si las propiedades de origen de enlace son del mismo tipo que las propiedades de destino de enlace; no hay necesidad de incluir un converter. Quiero ejemplificar esto agregando un enlace a datos a la propiedad Visibility del control btnFoto que se enlazará a la propiedad Visibility del control imgFoto, donde el control btnFoto será el objeto de destino de enlace y el control imgFoto será el objeto de origen de enlace. Una vez hechas las adecuaciones, quedará como a continuación se muestra:

<Button x:Name=”btnFoto” Width=”100″ Height=”25″

        Content=”Agregar Foto” Margin=”5″ Click=”btnFoto_Click”

        Visibility=”{Binding Visibility, ElementName=imgFoto}”/>

 

La funcionalidad esperada será que se oculten ambos controles al cambiar el valor de la propiedad IsChecked del control chkMostrarImagen a false y que se muestren al cambiar el valor de la propiedad IsChecked a true con solo utilizar un converter y el enlace a datos. Para ocultar el botón btnFoto no hay más que hacer que el enlace a datos de su propiedad Visibility a la propiedad Visibility del control imgFoto y se comportará como este último, esto es, que si es visible el control imgFoto, también será visible el control btnFoto y si se oculta el control imgFoto, también se ocultará el control btnFoto. Al final ya todo el código XAML de la ventana se verá de esta manera:

<Window x:Class=”EjemplosWPF.MainWindow”

        xmlns=”http://schemas.microsoft.com/winfx/2006/xaml/presentation”

        xmlns:x=”http://schemas.microsoft.com/winfx/2006/xaml”

        xmlns:conv =”clr-namespace:EjemplosWPF”

        Title=”MainWindow” Height=”350″ Width=”525″>

    <Window.Resources>

        <conv:ConvertByteArrayToBitmapImage

               x:Key=”ByteArrayToBitmapImage”/>

        <conv:ConvertBooleanToVisibilityCollapsed

               x:Key=”BooleanToVisibilityCollapsed”/>

    </Window.Resources>

    <Grid>

        <StackPanel HorizontalAlignment=”Center” VerticalAlignment=”Center”>

            <CheckBox x:Name=”chkMostrarImagen” Content=”Mostrar Imagen”

                        Margin=”5″ IsChecked=”True”/>

            <Image x:Name=”imgFoto” Width=”100″ Height=”100″ Margin=”5″

                    Source=”{Binding Image, UpdateSourceTrigger=PropertyChanged,

                            Mode=TwoWay,

                            Converter={StaticResource ByteArrayToBitmapImage}}”

                    Visibility=”{Binding IsChecked, ElementName=chkMostrarImagen,

                    Converter={StaticResource BooleanToVisibilityCollapsed}}”/>                  

            <Button x:Name=”btnFoto” Width=”100″ Height=”25″

                    Content=”Agregar Foto” Margin=”5″ Click=”btnFoto_Click”

                    Visibility=”{Binding Visibility, ElementName=imgFoto}”/>

        </StackPanel>

    </Grid>

</Window>

 

El código de la clase de la ventana, bueno, aunque lo manoseamos un poco, quedará como estaba en un principio. Lo único que agregamos fue la clase del converter y la declaración de los enlaces a datos.

Es una práctica muy común todavía la de escribir código para ocultar y mostrar los controles dentro de la clase de la ventana, y si había que ocultar o mostrar varios controles, había que escribir una línea para cada uno. Ahora con el enlace a datos entre controles, podemos propagar el valor de la propiedad de un control a todos los que deseemos con muy pocas líneas de código lo que resulta en un código más simple en la clase de la ventana.

Esto ha sido todo por esta ocasión y nos seguiremos leyendo después.

Octavio Telis

Leave a Reply

Your email address will not be published. Required fields are marked *


*

.NET Chronicles
  • Eventos del Teclado en WPF July 22, 2015
      Hola ¿qué tal? En esta ocasión como como continuidad a lo que previamente había escrito sobre los eventos del teclado en Windows Forms, haré un artículo sobre el uso de los eventos del teclado en WPF, así es en Windows Presentation Foundation. No es desconocido por muchos que el nuevo estándar de desarrollo de […]
  • Programación Orientada a Objetos (Introducción) May 14, 2014
    Hola qué tal… Aquí les dejo este video sobre la programación orientada a objetos, a manera de preámbulo a los siguientes videos, con las bases de la programación en C# y otra línea con algunas utilerías. Saludos… Octavio Telis
  • Capítulo piloto del la versión en video de .NET Chronicles May 6, 2014
    Hola qué tal??? Pues en esta ocasión estoy compartiendo con ustedes la liga de un video piloto, con el que pretendo comunicar un poco más sobre las tecnologías .NET y los lenguajes de Programación. En esta ocasión será con la presentación de C#, en un capitulo titulado “Te presento a C#”, espero que sea de […]
  • Validación de datos de entrada con enlace a datos en WPF May 27, 2012
    Tweet Hola, qué tal. Ya que he venido hablando del enlace a datos en WPF en las publicaciones anteriores, bien convendría considerar el uso del Binding para validar datos, hemos tocado ya algunos puntos necesarios para escribir esta funcionalidad. En las publicaciones anteriores vimos ya como enlazar los datos, además, cómo convertir los datos, ahora, […]
  • Uso de la propiedad Visibility con valores booleanos en WPF May 25, 2012
    Tweet    Hola que tal. En esta ocasión quiero compartir con ustedes una manera para utilizar la propiedad Visibility con un valor bool. Te preguntarás “¿Y qué sentido tiene eso?”, bien, el detalles está en que en ocasiones se requiere que la visibilidad de un control de la interfaz de usuario responda a un valor […]
  • Conversión de Valores con Enlace a Datos May 24, 2012
    Tweet     Hola que tal. En el diseño de aplicaciones con WPF tendremos muchas ventajas en cuanto a la interfaz de usuario se refiere, dado que es un modelo muy flexible, nos permite dar formato y estilo a la apariencia de la interfaz de usuario, logrando aplicaciones más agradables, vistosas y que mejoran por mucho […]
  • Mostrar archivos de imagen en WPF utilizando enlace a datos. May 23, 2012
    Tweet Holal Qué tal. Continuando con el ejemplo del post anterior (http://bit.ly/oh0m9k), referente al manejo de imágenes en WPF, ejemplificaremos el manejo de las imágenes de la misma manera pero, esta vez, utilizando enlace a datos y la clase Binding. Antes, tenemos que hablar un poco de lo que es el enlace a datos con […]
  • Mostrar archivos de imagen en WPF August 26, 2011
    Hola Qué Tal… En esta ocasión quiero comenzar una serie de artículos sobre el manejo de archivos de imágenes con .NET. En esta primera parte voy a tratar la manera de cargar un archivo de imagen en un contenedor de imagen, en este caso usaré WPF y el control Image que viene incluido en el […]
  • Comparar dos DataTables según sus DataRows August 23, 2011
    Hola que tal. En ocasiones es necesario comparar el contenido de dos DataTable para determinar qué registros (DataRow) están en una y en otra no. Supongamos tenemos dos DataTable; dt1 y dt2, ambas con el mismo esquema. La tabla dt2 contiene más registros que la taba dt1, por lo que deseamos saber qué registros de […]
  • Arquitectura – Definición de un Data Access Component (con un ejemplo) Parte 3 May 14, 2010
    Hola Qué Tal? En esta ocasión, no he dejado pasar tanto tiempo para terminar la trilogía del uso de Data Access Component con un ejemplo. Bien, pues en este artículo veremos el uso del componente ya creado, cómo extenderemos la funcionalidad del componente y cómo lo aplicamos en la interfaz de usuario. Primeramente, debemos crear […]