Отображение окна

20

Чтобы отобразить окно, необходимо создать экземпляр класса Window и вызвать метод Show() или ShowDialog().

Метод ShowDialog() отображает модальное окно. Модальные окна не позволяют пользователю получать доступ к родительскому окну, блокируя возможность использования в нем мыши и возможность ввода в нем каких-то данных до тех пор, пока модальное окно не будет закрыто. Вдобавок метод ShowDialog() не осуществляет возврат до тех пор, пока модальное окно не будет закрыто, так что выполнение любого находящегося после него кода на время откладывается. (Однако это не означает, что в данное время не может выполняться никакой другой код — например, при наличии запущенного таймера обработчик его событий все равно будет работать.) Наиболее часто применяемая в коде схема выглядит так: отображение модального окна, ожидание его закрытия и последующее выполнение над его данными какой-нибудь операции.

Ниже показан пример использования метода ShowDialog():

TaskWindow winTask = new TaskWindow();
winTask.ShowDialog();
// Выполнение достигает этой точки после закрытия winTask

Метод Show() отображает немодальное окно, которое не блокирует доступ пользователя ни к каким другим окнам. Более того, метод Show() производит возврат управления сразу после отображения окна, благодаря чему следующие за ним в коде операторы выполняются незамедлительно. Можно создавать и показывать сразу несколько немодальных окон, и пользователь может взаимодействовать со всеми ними одновременно.

В случае применения немодальных окон иногда требуется код синхронизации, гарантирующий обновление информации во втором окне при внесении каких-то изменений в первом и тем самым исключающий вероятность работы пользователя с недействительными данными.

Ниже показан пример использования метода Show():

MainWindow winMain = new MainWindow();
winMain.Show();
// Выполнение достигает этой точки сразу же после отображения winMain

Модальные окна идеально подходят для предоставления пользователю приглашения сделать выбор, прежде чем выполнение операции сможет быть продолжено. Например, возьмем приложение Microsoft Word. Это приложение всегда отображает окна Options (Параметры) и Print (Печать) в модальном режиме, вынуждая пользователя принимать решение перед продолжением. С другой стороны, окна, предназначенные для поиска по тексту или проверки наличия в документе орфографических ошибок, Microsoft Word отображает в немодальном режиме, позволяя пользователю редактировать текст в основном окне документа, пока идет выполнение задачи.

Закрывается окно точно так же просто, с помощью метода Close(). Альтернативным вариантом является сокрытие окна из вида путем использования метода Hide() или установки для свойства Visibility значения Hidden. И в том и в другом случае окно остается открытым и доступным для кода. Как правило, скрывать имеет смысл только немодальные окна. Дело в том, что при сокрытии модального окна код остается в "замороженном" состоянии до тех пор, пока окно не будет закрыто, а закрыть невидимое окно пользователь никак не сможет.

Позиционирование окна

Обычно размещать окно в каком-нибудь точно определенном месте на экране не требуется. В таких случаях можно просто указать для свойства WindowStartupLocation значение CenterOwner и ни о чем не беспокоится. В других случаях, которые хоть встречаются реже, но все-таки бывают, должна задаваться точная позиция окна, что подразумевает использование значения Manual для свойства WindowStartupLocation и указание точных координат в свойствах Left и Top.

Иногда выбору подходящего месторасположения и размера для окна нужно уделять немного больше внимания. Для примера рассмотрим следующую ситуацию: вы случайно создали окно с размером, который является слишком большим для отображения на дисплее с низким разрешением. Если речь идет о приложении с единственным окном, тогда наилучшим решением будет создать окно с возможностью изменения размеров. Если же речь идет о приложении с несколькими плавающими окнами, то дело усложняется.

Можно попробовать просто ограничить позиции окна теми, которые поддерживаются даже на самых маленьких мониторах, но это, скорее всего, будет раздражать пользователей мониторов более новых моделей (которые приобрели мониторы с большей разрешающей способностью специально для того, чтобы иметь возможность умещать на экране больше информации). Поэтому вероятнее всего придется принимать решение о наилучшем размещении окна во время выполнения. А для этого потребуется извлечь кое-какую базовую информацию о доступном оборудовании для отображения с помощью класса System.Windows.SystemParameters.

Класс SystemParameters поддерживает огромный список статических свойств, которые возвращают информацию о различных параметрах системы. Например, его можно использовать для определения, включил ли пользователь помимо всего прочего функцию "горячего" отслеживания (hot tracking) и возможность перетаскивания целых окон. В случае окон класс SystemParameters особенно полезен, потому что предоставляет два свойства, которые возвращают информацию о размерах текущего экрана: FullPrimaryScreenHeight и FullPrimaryScreenWidth. Оба они довольно просты, что иллюстрируется в показанном ниже коде (центрирующем окно во время выполнения):

double screeHeight = SystemParameters.FullPrimaryScreenHeight;
double screeWidth = SystemParameters.FullPrimaryScreenWidth;
this.Top = (screenHeight - this.Height) / 2;
this.Left = (screenWidth - this.Width) / 2;

Хотя этот код эквивалентен применению свойства WindowStartupLocation со значением CenterScreen, он предоставляет гибкость, позволяя реализовать различную логику позиционирования и выполнять ее в подходящее время.

Еще лучший вариант предусматривает применение прямоугольника SystemParameters.WorkArea для размещения окна в свободной области экрана. При вычислении рабочей области область, где пристыкована панель задач (и любые другие "полосы", стыкованные с рабочим столом), не учитывается.

double workHeight = SystemParameters.WorkArea.Height;
double workWidth = SystemParameters.WorkArea.Width;
this.Top = (workHeight - this.Height) / 2;
this.Left = (workWidth - this.Width) / 2;

Оба примера с позиционированием окна характеризуются одним небольшим недостатком. Когда свойство Тор устанавливается для окна, которое уже является видимым, это окно незамедлительно перемещается и обновляется. То же самое происходит и при установке свойства Left в следующей строке кода. В результате пользователям с хорошим зрением может быть заметно, что окно перемещается дважды. К сожалению, класс Window не предоставляет метода, который бы позволял устанавливать оба этих свойства одновременно. Поэтому единственным решением является позиционирование окна после его создания, но перед его отображением с помощью метода Show() или ShowDialog().

Сохранение и восстановление информации о местоположении окна

К числу типичных требований для окна относится и запоминание его последнего месторасположения. Эта информация может сохраняться как в конфигурационном файле пользователя, так и в системном реестре Windows.

Если нужно сделать так, чтобы информация о расположении какого-то важного окна хранилась в конфигурационном файле конкретного пользователя, дважды щелкните на узле Properties (Свойства) в окне Solution Explorer и выберите раздел Settings (Параметры). После этого добавьте действующий только на уровне данного пользователя параметр с типом данных System.Windows.Rect, как показано на рисунке:

Свойство для сохранения информации о расположении и размерах окна

При наличии такого параметра далее можно легко создать код, который будет автоматически сохранять информацию о размерах и расположении окна:

Properties.Settings.Default.WindowPosition = win.RestoreBounds;
Properties.Settings.Default.Save();

Обратите внимание, что в приведенном коде используется свойство RestoreBounds, которое предоставляет корректные размеры (т.е. последний размер окна в обычном состоянии — не свернутом и не развернутом), даже если в текущий момент окно развернуто или свернуто. (Эта удобная функция не была напрямую доступной в Windows Forms и требовала вызова неуправляемой API-функции GetWindowPlacement().)

Извлечь эту информацию, когда она необходима, тоже легко:

try
{
   Rect bounds = Properties.Settings.Default.WindowPosition;
   win.Top = bounds.Top;
   win.Left = bounds.Left;
   // Восстановить размеры, только если они
   // устанавливались для окна вручную
   if (win.SizeToContent == SizeToContent.Manual)
   {
      win.Width = bounds.Width;
      win.Height = bounds.Height;
   }
}
catch
{
   MessageBox.Show("Нет сохраненных параметров");
}

Единственным ограничением при таком подходе является необходимость создавать отдельное свойство для каждого окна, у которого должна сохраняться информация о расположении и размерах. Если требуется, чтобы информация о расположении сохранялась у множества различных окон, тогда, возможно, лучше будет разработать более гибкую систему.

Пройди тесты
Лучший чат для C# программистов