This article has been localized into Spanish by the community.
Creando un diálogo de entrada de datos personalizado
En los últimos artículos se han analizado algunos de los diálogos incorporados de WPF. Crear el propio es muy fácil, de hecho solo se necesita crear una ventana, colocar los controles y después mostrarla.
Sin embargo, hay algunas cosas que debes tener en cuenta al crear diálogos, de esta manera harás que tus aplicaciones funcionen de igual manera que otras aplicaciones de Windows. En este artículo crearemos un diálogo muy sencillo. Le haremos una pregunta al usuario y regresaremos una respuesta, mientras lo realizamos seguiremos las buenas práctica que tú también deberías seguir.
Diseñando el diálogo
Para este cuadro de diálogo en particular, solo quería una etiqueta que indicara al usuario qué información necesitamos de él/ella, un cuadro de texto para ingresar la respuesta y luego los típicos botones Aceptar y Cancelar. Decidí agregar un icono al diálogo, para una mejor apariencia. Te muestro resultado final:
Y aquí está el código del diálogo:
<Window x:Class="WpfTutorialSamples.Dialogs.InputDialogSample"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Input" SizeToContent="WidthAndHeight" WindowStartupLocation="CenterScreen"
ContentRendered="Window_ContentRendered">
<Grid Margin="15">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Image Source="/WpfTutorialSamples;component/Images/question32.png" Width="32" Height="32" Grid.RowSpan="2" Margin="20,0" />
<Label Name="lblQuestion" Grid.Column="1">Question:</Label>
<TextBox Name="txtAnswer" Grid.Column="1" Grid.Row="1" MinWidth="250">Answer</TextBox>
<WrapPanel Grid.Row="2" Grid.ColumnSpan="2" HorizontalAlignment="Right" Margin="0,15,0,0">
<Button IsDefault="True" Name="btnDialogOk" Click="btnDialogOk_Click" MinWidth="60" Margin="0,0,10,0">_Ok</Button>
<Button IsCancel="True" MinWidth="60">_Cancel</Button>
</WrapPanel>
</Grid>
</Window>
using System;
using System.Windows;
namespace WpfTutorialSamples.Dialogs
{
public partial class InputDialogSample : Window
{
public InputDialogSample(string question, string defaultAnswer = "")
{
InitializeComponent();
lblQuestion.Content = question;
txtAnswer.Text = defaultAnswer;
}
private void btnDialogOk_Click(object sender, RoutedEventArgs e)
{
this.DialogResult = true;
}
private void Window_ContentRendered(object sender, EventArgs e)
{
txtAnswer.SelectAll();
txtAnswer.Focus();
}
public string Answer
{
get { return txtAnswer.Text; }
}
}
}
El código es bastante simple, pero aquí están las cosas a las que deberías prestarle especial atención:
XAML
En la parte de XAML, he utilizado un Grid para la disposición de los controles - nada lujoso aquí. He quitado las propiedades Width y Height del Window y en su lugar lo designé para redimensionarse automáticamente para ajustarse al contenido - esto tiene sentido en un diálogo, así no tienes que ajustar detalladamente el tamaño para hacer que todo se vea bien. En cambio, usa márgenes y tamaños mínimos para asegurar que las cosas se ven de la forma que tu quieres, a la vez que se permite al usuario redimensionar el diálogo.
Otra propiedad que he cambiado en el Window es la propiedad WindowStartupLocation. Para un diálogo como este, y probablemente para la mayoría de los otros que no son ventanas principales, deberías cambiar este valor por CenterScreen o CenterOwner, para cambiar el comportamiento por omisión con el que tu ventana aparecerá en una posición elegida por Windows, a menos que tu especifiques manualmente las propiedades Top y Left para ella.
También presta especial atención a las dos propiedades que he usado en los botones de diálogo: IsCancel y IsDefault. IsCancel le indica a WPF que si el usuario hace click en el botón, el campo DialogResult de Window deberá ponerse a false lo cual cerrará también la ventana. Esto asgura que el usuario puede presionar la tecla Esc en su teclado para cerrar la ventana, algo que siempre debería ser posible en una ventana de dialogo de Windows.
La propiedad IsDefault provee el foco al botón Ok y asegura que si el usuario presiona la tecla Enter en su teclado, el botón se activa. Aunque se necesita un event handler para activar DialogResult, como se describe después.
Código detrás
En el área de codigo, he cambiado el contructor para que pueda recibir dos parametros, uno de ellos es opcional. Esto nos permite colocar la pregunta y la respuesta por omisión. Si la respuesta se provee, será colocada en los controles de la interfaz de usuario.
El botón Ok tiene un manipulador de eventos que asegura que la propiedad especial DialogResult de la ventana se le asigne true cuando se la haga clic, esta es la señal al iniciador del diálogo que el usuario ha aceptado el valor entrado. No tenemos uno para el botón de cancelar, porque WPF manipula esto por nosotros cuando la asignamos true a la propiedad IsCancel, como se describe arriba.
Para darle el foco al TextBox cuando se muestre el diálogo, me he subscrito al evento ContentRendered, donde selecciono todo el texto en el control y entonces le asigno el foco. Si usted desea asignarle el foco, se puede usar la popiedad anexada FocusManager.FocusedElement en la ventana, pero en este caso, yo tambien deseo seleccionar el texto, para permitirle al usuario sobreescribir instantaneamente la respuesta provista por omision (si hay).
Un último detalle es la propiedad Answer que he implementado. Símplemente da acceso al valor del control TextBox, pero es una buena práctica el proveer una propiedad que nos retorne el/los valor/es del diálogo, en vez de acceder al control directamente desde fuera de la ventana. Esto además permite moldear el valor de retorno antes de que se devuelva, si se necesita.
Usando el diálogo
Con todo lo de arriba en su sitio, estamos listos para empezar a utilizar nuestro diálogo. Es una tarea bastante sencilla, así que he creado una pequeña aplicación para probarlo. Este es el código:
<Window x:Class="WpfTutorialSamples.Dialogs.InputDialogAppSample"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="InputDialogAppSample" Height="150" Width="300">
<StackPanel HorizontalAlignment="Center" VerticalAlignment="Center">
<TextBlock>Hello, world. My name is:</TextBlock>
<TextBlock Name="lblName" Margin="0,10" TextAlignment="Center" FontWeight="Bold">[No name entered]</TextBlock>
<Button Name="btnEnterName" Click="btnEnterName_Click">Enter name...</Button>
</StackPanel>
</Window>
using System;
using System.Windows;
namespace WpfTutorialSamples.Dialogs
{
public partial class InputDialogAppSample : Window
{
public InputDialogAppSample()
{
InitializeComponent();
}
private void btnEnterName_Click(object sender, RoutedEventArgs e)
{
InputDialogSample inputDialog = new InputDialogSample("Please enter your name:", "John Doe");
if(inputDialog.ShowDialog() == true)
lblName.Text = inputDialog.Answer;
}
}
}
No hay nada en especial - sólo un par de controles TextBlock y un Button para invocar el diálogo. En el manejador del evento Click, instanciamos la ventana InputDialogSample, proporcionando una pregunta y una respuesta por defecto, y entonces utilizamos el método ShowDialog() para mostrarlo - Siempre deberías utilizar el método ShowDialog() y no sólo el Show() para diálogos modales como este.
Si el resultado del dialog es true, quiere decir que el usuario o bien ha activado el botón OK o ha presionado la tecla Enter, y el resultado se asigna al nombre Label. Eso es todo!