Добавить в цитаты Настройки чтения

Страница 8 из 15



Следует заметить, что полученная программа содержит серьезную ошибку, которая будет исправлена в следующем пункте.

Комментарии

1. Благодаря явному заданию значения false для свойств ShowInTaskbar подчиненных окон, кнопки для этих окон не отображаются на панели задач в нижней части экрана.

2. За возможность изменения размеров окна и отображение кнопок минимизации/максимизации на его заголовке отвечает свойство ResizeMode, которое может принимать следующие значения: NoResize (размер окна фиксирован, кнопки не отображаются), CanMinimize (размер окна фиксирован, доступна кнопка минимизации), CanResize (значение по умолчанию: окно может менять размер, доступны обе кнопки), CanResizeWithGrip (то же, что и CanResize, но в правом нижнем углу окна дополнительно отображается треугольный маркер; благодаря этому маркеру увеличивается область, которую можно зацепить мышью для изменения размеров окна). Для диалоговых окон дополнительно следует установить свойство WindowStyle равным ToolWindow; это обеспечивает скрытие иконки на заголовке окна (отображать иконки в диалоговых окнах не принято).

3. Присваивание свойству Owner некоторого окна w1, значения какого-либо другого окна w0 делает окно w1 подчиненным по отношению к главному окну w0. Подчиненное окно всегда отображается поверх главного (даже если главное окно является активным). Кроме того, при минимизации или закрытии главного окна его подчиненные окна также минимизируются (или, соответственно, закрываются). Следует заметить, что свойству Owner можно присвоить значение только такого окна, которое уже отображено на экране, поэтому указанные действия мы выполняем в обработчике события Loaded, которое возникает при первом отображении окна.

4. За начальное расположение окна на экране отвечает свойство WindowStartupLocation, равное по умолчанию значению Manual. При этом позицию окна можно задать явно с помощью свойств Left и Top или не задавать эти свойства, оставив определение начальной позиции на усмотрение операционной системы. В последнем случае «истинные» значения свойств Left и Top будут доступны только в момент первого отображения окна на экране. Как уже было отмечено в предыдущем комментарии, с этой ситуацией связано событие окна Loaded, поэтому начальное положение окна win1 определяется нами в обработчике данного события для главного окна.

5. Содержимое файла MainWindow.xaml демонстрирует традиционный для технологии WPF динамический способ размещения компонентов в окне, при котором их размеры и положение (а также иногда и размеры окна) определяются автоматически на основе указанных настроек. В данном случае в окне надо разместить две кнопки по вертикали. Такой способ размещения проще всего обеспечить с помощью группирующего компонента StackPanel (данный компонент имеет свойство Orientation с вариантами значений Vertical и Horizontal, причем первый вариант является значением по умолчанию).

Для указания полей – промежутков между компонентами – используется свойство Margin, которое может состоять из 1, 2 или 4 значений. Единственное значение определяет одинаковое поле (в аппаратно-независимых единицах, равных 1/96 дюйма) во всех направлениях, при наличии двух значений первое определяет поле слева и справа, а второе – сверху и снизу, при наличии четырех значений поля определяются в следующем порядке: левое, верхнее, правое, нижнее. Обратите внимание на то, что для того, чтобы обеспечить одинаковые промежутки (равные 10 единицам) как между компонентами, так и между компонентом и границей окна, следует задать поля, равные 5, как для группирующего (невидимого) компонента, так и для содержащихся в нем видимых компонентов-кнопок.

Помимо «внешних полей» (margins) для компонентов можно задавать «внутренние поля» (paddings), определяющие расстояние от границы компонента до его содержимого. Внутренние поля определяются свойством Padding, которое задается по тем же правилам, что и свойство Margin.

Следует также обратить внимание на значение свойства MinWidth, которое задано только для первой кнопки. Оно определяет минимальную ширину данного компонента и тем самым минимальную ширину всей панели StackPanel, причем все остальные компоненты на этой панели будут иметь такую же ширину. Таким образом, реальные размеры как кнопок, так и панели будут определяться размером шрифта, используемого для надписей на кнопках. Если шрифт велик настолько, что текст по ширине будет превосходить указанную минимальную ширину в 200 единиц, то свойство MinWidth будет проигнорировано и ширина кнопки станет больше 200 единиц; при этом кнопка по-прежнему будет иметь указанные внутренние и внешние поля.



В окнах, подобных главному окну из нашего проекта, желательно, чтобы их размер подстраивался под размер содержимого (в данном случае – панели StackPanel). Для этого предусмотрено свойство окна SizeToContent, которое мы положили равным WidthAndHeight (можно также подстраивать под размер содержимого только ширину или только высоту окна). По умолчанию данное свойство равно Manual, в этом случае не окно подстраивается под свое содержимое, а наоборот – компоненты подстраиваются под размер окна. Заметим, что если оставить в xaml-файле атрибуты Width и Height для окна, то в окне дизайнера окно будет иметь указанные размеры даже при наличии атрибута SizeToContent, равного WidthAndHeight, однако при выполнении программы явно указанные размеры окна будут игнорироваться.

2.2. Решение проблем, возникающих при повтором открытии подчиненных окон

Ошибка. После закрытия окна win1 или win2 попытка его повторного открытия приводит к исключению с диагностикой «Нельзя задать Visibility или вызвать Show, ShowDialog или WindowInteropHelper.EnsureHandle после закрытия окна»). Это связано с тем, что закрытие окна, открытого в любом режиме, приводит к его разрушению (заметим, что в библиотеке Windows Forms подобная ситуация имеет место только для окон, открытых в обычном режиме, разрушения же окон, открытых в диалоговом режиме, не происходит).

Исправление. Для классов Window1 и Window2 определите следующие одинаковые обработчики события Closing:

Window1.xaml.cs и Window2.xaml.cs:

Результат. Теперь окна win1 и win2 можно многократно закрывать и открывать в ходе выполнения программы.

Комментарии

1. Событие Closing относится к группе событий, которые возникают перед выполнением некоторого действия и позволяют отменить его (имена этих событий оканчиваются на -ing). Второй параметр e у обработчиков подобных событий имеет изменяемое свойство Cancel, которому следует присвоить значение true, если требуется отменить соответствующее действие. В приведенном обработчике отменяется закрытие окна; вместо этого оно просто удаляется с экрана методом Hide (аналогичного результата можно добиться, установив значение его свойства Visibility равным значению Visibility.Hidden). Заметим, что сделанное изменение не препятствует «настоящему» закрытию подчиненных окон при закрытии главного окна приложения.

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