TOC

This article has been localized into Russian by the community.

Диалоговые окна:

Создание собственного окна-редактора

В последних главах руководства мы познакомились с использованием диалоговых окон, встроенных в WPF. Создание собственных так же является довольно простым процессом. Для этого Вам просто необходимо создать Окно, добавить требуемые элементы управления и показать его.

Однако, здесь существует несколько подводных камней, о которых стоит помнить при разработке собственных диалоговых окон, для обеспечения их работы подобно как в других приложениях Windows. В этой статье мы создадим крайне простое окно, для опроса пользователя и возврата его ответа, при этом мы рассмотрим "лучшие практики", которым необходимо следовать.

Проектирование диалогового окна

Я хочу, чтобы Label в моем окне передал пользователю, какую информацию я хочу от него получить (TextBox для ответа) и добавлю стандартные кнопки Ok и Cancel. Еще я решил добавить иконку в окно, для более изящного вида. Вот результат:

А это код диалогового окна:

<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; }
		}
	}
}

Код достаточно простой, но в нем есть несколько моментов, на которые стоит обратить внимание:

XAML

В разметке XAML я использовал Grid в качестве контейнера для элементов - ничего удивительного. Я удалил свойства Width и Height элемента Window и вместо этого установил их как Auto, для автоматического подбора размера под размеры контента (что, конечно же имеет смысл при проектировании диалогового окна), этот механизм освобождает Вас от необходимости подбора размеров, при сохранении хорошего внешнего вида. Вместо этих свойств используйте поля и минимальные размеры, для того, чтобы диалоговое окно выглядело так как Вы хотите, при изменении пользователем его размеров.

Следующим свойством Window, которое я изменил является WindowStartupLocation. Для подобного диалогового окна, вероятно как и для большинства нестандартных окон, Вам следует изменить его значение на CenterScreen или CenterOwner, для определения поведения по умолчанию (позицией окна будет управлять Windows), вместо того, чтобы "вручную" определять свойства Top и Left.

Уделите внимание двум свойствам, которые я определил для кнопок окна: IsCancel и IsDefault. IsCancel сообщает WPF, что если пользователь нажмет на нее, то DialogResult Окна надо будет определить как false, что также закроет окно. Вдобавок, это гарантирует, что если пользователь нажмет Esc - окно закроется (учитывая то, что подобный функционал всегда будет реализован в стандартных окнах Windows).

В свою очередь свойство IsDefault направляет фокус на кнопку Ok, и, также гарантирует, что если пользователь нажмет Enter, эта кнопка сработает. Для реализации подобного механизма, необходимо определить обработчик событий для DialogResult, что будет оговорено позже.

Code-behind

В Code-behind я определил конструктор с двумя входными параметрами, один из которых является опциональным. Это позволяет разместить вопрос и ответ по умолчанию (если предусмотрен) в элементы пользовательского интерфейса.

Кнопка Ok имеет обработчик событий, который гарантирует, что при нажатии на нее, свойство Окна DialogResult будет определено как true, для сигнализации инициатора диалогового окна о том, что пользователь подтвердил введенное значение. Мы не нуждаемся в имплементации кнопки Cancel, так как WPF сделает это за нас (при свойстве IsCancel равном true), как было описано выше.

Для того, чтобы во время открытия окно сфокуировалось на элементе TextBox, я подписался на событие ContentRendered, в котором выбрал весь текст в TextBox и передал последнему фокус. Если я захочу просто передать фокус, я использую свойство окна FocusManager.FocusedElement, но в нашем примере, я так же хотел выбрать весь текст из TextBox, чтобы позволить пользователю моментально переопределить ответ, предлагаемый по умолчанию (если он существует, конечно).

Последняя деталь - свойство Answer, которое я определил. Оно просто дает доступ к введенному в элемент TextBox значению, что является "хорошей практикой", предоставляющей свойство с возвращаемым значением(значениями) диалогового окна, вместо прямого доступа к элементу извне. Это также позволяет Вам повлиять на выходной параметр, до того, как он будет возвращен (если Вам это нужно.)

Использование диалогового окна

Совместив все описанное выше вместе, мы наконец-то можем использовать наше окно. Это достаточно простая задача, я создал простое приложение для его тестирования. Вот код:

<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;
		}
	}
}

Здесь нет ничего сверхъестественного - просто несколько элементов TextBlock и кнопка для вызова окна. В обработчике событий Click, мы создаем окно InputDialogSample, предоставляя пользователю вопрос и ответ по умолчанию, а после используем метод ShowDialog() для отображения диалогового окна (всегда следует использовать метод ShowDialog(), а не просто Show(), для подобных модальных окон).

Если результат окна равняется true, это значит, что пользователь нажал на кнопку Ok либо на Enter, а результат присвоился элементу Label, отвечающему за отображение имени. Вот и все!


This article has been fully translated into the following languages: Is your preferred language not on the list? Click here to help us translate this article into your language!