| исключение в лабораторной работе № 3 |
Графика в WPF
-
Запустите приложение
- первое окно будет выглядеть так
Обратите внимание, что окно не имеет системных кнопок минимизации и максимизации, а только кнопку закрытия. Это поведение обеспечивается настройкой в разметке окна с помощью атрибута ResizeMode="NoResize".
При наведении курсора на окно работает всплывающая подсказка, напоминающая способы создания дочернего окна. Но пока обработчики, предназначенные для этой функциональности, у нас пустые, да и само окно еще нужно заготовить.
-
Добавьте к
текущему (выделенному в панели Solution Explorer ) проекту WpfApp3 командой Project/Add Window новое окно WPF с
именем Window2.xaml
-
Наполните файл Window2.xaml следующей
разметкой
<Window x:Class="WpfApp3.Window2"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Дочернее окно Window2: Работа объекта ImageDrawing через разметку"
SizeToContent="WidthAndHeight"
ResizeMode="NoResize"
Background="Green"
>
<Border BorderBrush="White" BorderThickness="2"
Margin="10"
Background="White"
>
<Image Stretch="None">
<Image.Source>
<DrawingImage>
<DrawingImage.Drawing>
<DrawingGroup>
<!-- Рис.1 - левый верхний (x, y, width, height) -->
<ImageDrawing Rect="0,0,348,232" ImageSource="Images\market 031.jpg"/>
<!-- Рис.2 - правый верхний (x, y, width, height) -->
<ImageDrawing Rect="350,0,348,232" ImageSource="Images\market 034.jpg"/>
<!-- Рис.3 - левый нижний (x, y, width, height) -->
<ImageDrawing Rect="0,234,348,232" ImageSource="Images\market 039.jpg"/>
<!-- Рис.4 - правый нижний (x, y, width, height) -->
<ImageDrawing Rect="350,234,348,232" ImageSource="Images\market 040.jpg"/>
</DrawingGroup>
</DrawingImage.Drawing>
</DrawingImage>
</Image.Source>
</Image>
</Border>
</Window>-
Дополните конструктор
класса Window2 следующим кодом
public Window2()
{
// Инициализация разметочной части
InitializeComponent();
// Корректировка заголовка окна
this.Title += "=\"Так голодают буржуины!\"";
// Дочернее окно не отображать в панели задач ОС
this.ShowInTaskbar = false;
}-
Заполните обработчики
щелчка и контекстного меню
в классе Window1 следующим кодом
Window wnd2;
private void Show_Window2(object sender, MouseButtonEventArgs e)
{
wnd2 = new Window2();
wnd2.Show();
}
private void Create_Window2(object sender, RoutedEventArgs e)
{
wnd2 = new Window2();
wnd2.Show();
}-
Запустите приложение
и вызовите окно Window2 через окно Window1, как напоминает всплывающая подсказка -
Получится следующий
результат
Здесь есть несколько существенных недостатков, которые следует устранить:
- Второе окно можно создавать во многих экземплярах
- Первое окно со вторым никак не связано и каждое из них закрывается самостоятельно
- Пиктограмма вторичных окон не отображается в панели задач (мы специально ввели в конструктор окна настройку this.ShowInTaskbar = false; )
Во первых, зачем пользователю много экземпляров одного и того же окна. Во вторых, хоть окна не имеют системных кнопок минимизации, все равно их можно минимизировать все сразу через операционную систему командой "Показать рабочий стол". А в этом случае вновь сделать видимыми вторичные окна не удасться и придется их закрывать через Диспетчер задач (либо через оболочку Visual Studio останавливать процесс - работу приложения).
Для устранения указанных недостатков нужно выполнить следующее:
- Назначить первое окно главным, чтобы при его закрытии закрывалось приложение в целом
- Блокировать создание вторичных окон, если одно из них уже существует, и попутно активировать вторичное окно, если оно заслонено другими окнами или скрыто
-
Для назначения
окна Window1 главным добавьте в его конструктор следующий код
public Window1()
{
// Инициализация разметочной части
InitializeComponent();
// Корректировка заголовка окна
this.Title += "=\"Так голодают буржуины!\"";
// Всплывающая подсказка
this.ToolTip = "Вызывайте дочернее окно\n"
+ "двойным щелчком мыши\n"
+ "или контекстным меню...";
// Сделать главным окном приложения
Application.Current.ShutdownMode = ShutdownMode.OnMainWindowClose;
Application.Current.MainWindow = this;
}-
Запустите
приложение и убедитесь, что теперь все дочерние окна при закрытии первичного
окна тоже закрываются вместе с приложением
Для блокирования повторного создания дочерних окон применим два способа:
- В первом способе обернем в классе Window1 ссылку wnd2 на дочернее окно в свойство Wnd2 и будем ее обнулять в обработчике Closed окна Window2. В первом окне перед созданием дочернего будем эту ссылку проверять - если ненулевая, значит окно существует и новое создавать нельзя
- Второй способ намного проще, так как не требует жесткой зависимости кода между окнами введением дополнительного общего поля или свойства. Можно просто воспользоваться свойством Application.Current.Windows типа WindowCollection приложения и в первом окне перед созданием дочернего проверять, отсутствует ли в коллекции существующих окон приложения интересующее нас окно. Любое созданное окно приложения сразу заносится в эту коллекцию, а при закрытии между событиями Closing и Closed немедленно удаляется из нее. Единственное, что потребуется определить в дочернем окне, так это свойство Name, по которому мы будем искать это окно в коллекции приложения
Итак, реализуем сказанное...
Способ 1
-
В классе Window1 найдите
объявление поля wnd2 (строка: Window wnd2; ), щелкните на поле wnd2 правой
кнопкой мыши и выполните команду контекстного меню Refactor/Encapsulate Field
Появится окно Encapsulate Field, предлагающее обернуть поле в общедоступное свойство с именем Wnd2
-
Скажите OK и оболочка
создаст следующий код свойства
public static Window Wnd2
{
get { return Window1.wnd2; }
set { Window1.wnd2 = value; }
}Аксессор get нам не нужен, поскольку в клиенте Window2 при закрытии окна мы будем поле wnd2 только обнулять через свойство Wnd2, поэтому get следует удалить.
-
Внесите в класс Window1 следующий окончательный код реализации первого способа
// Предотвращение повторного открытия окна: Способ 1
static Window wnd2;
public static Window Wnd2
{
set { Window1.wnd2 = value; }
}
// Обработчик двойного щелчка
private void Show_Window2(object sender, MouseButtonEventArgs e)
{
if (wnd2 == null)
{
wnd2 = new Window2();
wnd2.Show();
}
else
wnd2.Activate();// Сдвинуть на передний план
}-
Модифицируйте класс Window2 следующим образом
public partial class Window2 : Window
{
public Window2()
{
// Инициализация разметочной части
InitializeComponent();
// Корректировка заголовка окна
this.Title += "=\"Так голодают буржуины!\"";
// Дочернее окно не отображать в панели задач ОС
this.ShowInTaskbar = false;
// Регистрация обработчика
this.Closed += new EventHandler(Window2_Closed);
}
private void Window2_Closed(object sender, EventArgs e)
{
Window1.Wnd2 = null; // Для предотвращения повторного запуска
GC.WaitForFullGCComplete(); // Ждать завершения сборки мусора
GC.Collect(); // Начать сборку мусора
}
}Здесь мы немного перестарались и ввели еще принудительный вызов сборщика мусора. Это необязательно, так как запуск механизма освобождения брошенных ссылок активизирует сама среда исполнения и в том случае, когда заканчивается выделенная домену приложения оперативная память.
-
Запустите приложение
и убедитесь, что обработчик создания дочернего окна по двойному щелчку теперь
работает правильно
Способ 2
-
В разметку файла Window2.xaml добавьте определение свойства Name окна Window2 для его идентификации в коллекции
приложения
<Window x:Class="WpfApp3.Window2"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Name="Window_2"
Title="Дочернее окно Window2: Работа объекта ImageDrawing через разметку"
SizeToContent="WidthAndHeight"
ResizeMode="NoResize"
Background="Green"
>
...................................
</Window>-
Модифицируйте обработчик
контекстного меню в файле Window1.xaml.cs следующим образом
// Предотвращение повторного открытия окна: Способ 2
// Обработчик контекстного меню
private void Create_Window2(object sender, RoutedEventArgs e)
{
bool windowExists = false;
foreach (Window window in Application.Current.Windows)
{
if (window.Name == "Window_2")
{
windowExists = true;
window.Activate();// Сдвинуть на передний план
break;
}
}
if (!windowExists)
{
wnd2 = new Window2();
wnd2.Show();
}
}-
Запустите приложение
и убедитесь, что и этот обработчик блокирования повторного запуска
теперь тоже работает правильно -
Разберитесь с кодом
В будущих упражнениях мы продолжим знакомство с графическими возможностями WPF.



