При выполнении в лабораторной работе упражнения №1 , а именно при выполнении нижеследующего кода: using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Text; using System.Windows.Forms; using Microsoft.Xna.Framework.Graphics;
namespace Application1 { public partial class MainForm : Form { // Объявим поле графического устройства для видимости в методах GraphicsDevice device;
public MainForm() { InitializeComponent();
// Подпишемся на событие Load формы this.Load += new EventHandler(MainForm_Load);
// Попишемся на событие FormClosed формы this.FormClosed += new FormClosedEventHandler(MainForm_FormClosed); }
void MainForm_FormClosed(object sender, FormClosedEventArgs e) { // Удаляем (освобождаем) устройство device.Dispose(); // На всякий случай присваиваем ссылке на устройство значение null device = null; }
void MainForm_Load(object sender, EventArgs e) { // Создаем объект представления для настройки графического устройства PresentationParameters presentParams = new PresentationParameters(); // Настраиваем объект представления через его свойства presentParams.IsFullScreen = false; // Включаем оконный режим presentParams.BackBufferCount = 1; // Включаем задний буфер // для двойной буферизации // Переключение переднего и заднего буферов // должно осуществляться с максимальной эффективностью presentParams.SwapEffect = SwapEffect.Discard; // Устанавливаем размеры заднего буфера по клиентской области окна формы presentParams.BackBufferWidth = this.ClientSize.Width; presentParams.BackBufferHeight = this.ClientSize.Height;
// Создадим графическое устройство с заданными настройками device = new GraphicsDevice(GraphicsAdapter.DefaultAdapter, DeviceType.Hardware, this.Handle, presentParams); }
protected override void OnPaint(PaintEventArgs e) { device.Clear(Microsoft.Xna.Framework.Graphics.Color.CornflowerBlue);
base.OnPaint(e); } } } Выбрасывается исключение: Невозможно загрузить файл или сборку "Microsoft.Xna.Framework, Version=3.0.0.0, Culture=neutral, PublicKeyToken=6d5c3888ef60e27d" или один из зависимых от них компонентов. Не удается найти указанный файл. Делаю все пунктуально. В чем может быть проблема? |
Графика в 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.