Опубликован: 05.08.2010 | Доступ: свободный | Студентов: 2010 / 47 | Оценка: 4.50 / 4.40 | Длительность: 60:26:00
Самостоятельная работа 12:

События и команды в WPF

Размещение вариантов заголовков окна в ресурсах приложения

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

  • Откройте файл разметки App.xaml и дополните его определением ресурсов для хранения неизменяемой части заголовка окна. Областью видимости этих ресурсов будет все приложение
<Application x:Class="Notepad1.App"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    StartupUri="Window1.xaml">
    <Application.Resources>
        <String xmlns="clr-namespace:System;assembly=Mscorlib" x:Key="ApplicationTitle1">
            Title="Window1: Управление состоянием источников команд"
        </String>
        <String xmlns="clr-namespace:System;assembly=Mscorlib" x:Key="ApplicationTitle2">
            Title="Window2: Управление состоянием источников команд"
        </String>
    </Application.Resources>
</Application>

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

Будем мы использовать механизм команд WPF или нет, но без событий и их обработчиков никак не обойтись. Начнем с того факта, что для решения наших задач можно создать обработчики с одинаковой сигнатурой, определяемой делегатом пространства имен System.Windows:

public delegate void RoutedEventHandler(object sender, RoutedEventArgs e)

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

  • Добавьте в часть класса Window1, расположенную в файле File.cs, следующие пустые обработчики по количеству задач меню в разделе File
using System;
using System.Collections.Generic;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
    
namespace Notepad1
{
    partial class Window1
    {
        //------------------------------------------------------
        //
        //  Обработчики источников задач File
        //
        //------------------------------------------------------
    
        private void NewOnExecute(object sender, RoutedEventArgs e)
        {
        }
    
        private void OpenOnExecute(object sender, RoutedEventArgs e)
        {
        }
    
        private void SaveOnExecute(object sender, RoutedEventArgs e)
        {
        }
    
        private void SaveAsOnExecute(object sender, RoutedEventArgs e)
        {
        }
    
        private void PageSetupOnExecute(object sender, RoutedEventArgs e)
        {
        }
    
        private void PrintPreviewOnExecute(object sender, RoutedEventArgs e)
        {
        }
    
        private void PrintOnExecute(object sender, RoutedEventArgs e)
        {
        }
    
        private void ExitOnExecute(object sender, RoutedEventArgs e)
        {
        }
    }
}
  • Добавьте в часть класса Window1, расположенную в файле Edit.cs, следующие пустые обработчики по количеству задач меню в разделе Edit
using System;
using System.Collections.Generic;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
    
namespace Notepad1
{
    partial class Window1
    {
        //------------------------------------------------------
        //
        //  Обработчики источников задач Edit
        //
        //------------------------------------------------------
    
        private void UndoOnExecute(object sender, RoutedEventArgs e)
        {
        }
    
        private void RedoOnExecute(object sender, RoutedEventArgs e)
        {
        }
    
        private void CutOnExecute(object sender, RoutedEventArgs e)
        {
        }
    
        private void CopyOnExecute(object sender, RoutedEventArgs e)
        {
        }
    
        private void PasteOnExecute(object sender, RoutedEventArgs e)
        {
        }
    
        private void DeleteOnExecute(object sender, RoutedEventArgs e)
        {
        }
    
        private void FindOnExecute(object sender, RoutedEventArgs e)
        {
        }
    
        private void FindNextOnExecute(object sender, RoutedEventArgs e)
        {
        }
    
        private void ReplaceOnExecute(object sender, RoutedEventArgs e)
        {
        }
    
        private void GoToOnExecute(object sender, RoutedEventArgs e)
        {
        }
    
        private void SelectAllOnExecute(object sender, RoutedEventArgs e)
        {
        }
    }
}
  • Добавьте в часть класса Window1, расположенную в файле Other.cs, следующие пустые обработчики по количеству задач меню в оставшихся разделах
using System;
using System.Collections.Generic;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
    
namespace Notepad1
{
    partial class Window1
    {
        //------------------------------------------------------
        //
        //  Прочие обработчики
        //
        //------------------------------------------------------
    
        private void FontOnExecute(object sender, RoutedEventArgs e)
        {
        }
    
        private void WordWrapOnExecute(object sender, RoutedEventArgs e)
        {
        }
    
        private void AboutOnExecute(object sender, RoutedEventArgs e)
        {
        }
    }
}
  • Запустите приложение и убедитесь, что ошибок компиляции нет, но функциональности от выполненной нами работы в нем пока не прибавилось

Регистрация обработчиков в разметке

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

  • Подключите декларативно в файле разметки Window1.xaml к каждому источнику события Click соответствующий обработчик и назначьте имена элементам для последующего управления ими из кода

Обратите внимание на некоторую некорректность работы подсказчика кода IntelliSense, который не предлагает нам список уже созданных обработчиков. Он ищет их в застраничном файле Window1.xaml.cs, в то время как обработчики находятся в других файлах частичного класса. В данном случае это не слишком большое неудобство, поскольку мнемоника составления имен у нас строго соблюдена и мы вряд ли ошибемся. Но в больших практических проектах это следует иметь ввиду, прежде чем разбивать класс окна по отдельным файлам.

Разметка с подключенными обработчиками и присвоенными именами будет выглядеть так (файл приводится целиком)

<Window x:Class="Notepad1.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        
    Title="Window1: Управление состоянием источников команд"
    Width="500" Height="375"
    MinWidth="500" MinHeight="375"
    WindowStartupLocation="CenterScreen"
    ResizeMode="CanResizeWithGrip"
    Loaded="Window_Loaded"
    Icon="Notepad.ico"
        >
    
    <Window.Resources>
        <!-- File -->
        <Image x:Shared="False" x:Key="iconNew"  Source="Images/NewDocumentHS.png" Width="16" Height="16" />
        <Image x:Shared="False" x:Key="iconOpen" Source="Images/OpenHS.png" Width="16" Height="16" />
        <Image x:Shared="False" x:Key="iconSave" Source="Images/SaveHS.png" Width="16" Height="16" />
        <Image x:Shared="False" x:Key="iconPageSetup" Source="Images/PrintSetupHS.png" Width="16" Height="16" />
        <Image x:Shared="False" x:Key="iconPrintPreview" Source="Images/PrintPreviewHS.png" Width="16" Height="16" />
        <Image x:Shared="False" x:Key="iconPrint" Source="Images/PrintHS.png" Width="16" Height="16" />
        <!-- Edit -->
        <Image x:Shared="False" x:Key="iconUndo" Source="Images/Edit_UndoHS.png" Width="16" Height="16" />
        <Image x:Shared="False" x:Key="iconRedo" Source="Images/Edit_RedoHS.png" Width="16" Height="16" />
        <Image x:Shared="False" x:Key="iconCut" Source="Images/CutHS.png" Width="16" Height="16" />
        <Image x:Shared="False" x:Key="iconCopy" Source="Images/CopyHS.png" Width="16" Height="16" />
        <Image x:Shared="False" x:Key="iconPaste" Source="Images/PasteHS.png" Width="16" Height="16" />
        <Image x:Shared="False" x:Key="iconDelete" Source="Images/DeleteHS.png" Width="16" Height="16" />
        <Image x:Shared="False" x:Key="iconFind" Source="Images/FindHS.png" Width="16" Height="16" />
        <Image x:Shared="False" x:Key="iconFont" Source="Images/FontHS.png" Width="16" Height="16" />
    </Window.Resources>
    
    <DockPanel LastChildFill="True">
        <!-- Меню -->
        <Menu DockPanel.Dock="Top">
            <MenuItem Header="_File">
                <!-- Сокращенные варианты подключения иконок с использованием статических ресурсов -->
                <MenuItem Name="itemNew" Click="NewOnExecute" Header="_New" InputGestureText="Ctrl+N" 
    Icon="{StaticResource iconNew}" />
                <MenuItem Name="itemOpen" Click="OpenOnExecute" Header="_Open..." InputGestureText="Ctrl+O" 
    Icon="{StaticResource iconOpen}" />
                <MenuItem Name="itemSave" Click="SaveOnExecute" Header="_Save" InputGestureText="Ctrl+S" 
     Icon="{StaticResource iconSave}" /> 
                <MenuItem Name="itemSaveAs" Click="SaveAsOnExecute" Header="Save _As..." />
                <Separator />
                <MenuItem Name="itemPageSetup" Click="PageSetupOnExecute" 
   Header="Page Set_up..." Icon="{StaticResource iconPageSetup}" />
                <MenuItem Name="itemPrintPreview" Click="PrintPreviewOnExecute" Header="P_rint Preview" 
  InputGestureText="Ctrl+F2" Icon="{StaticResource iconPrintPreview}" />
                <MenuItem Name="itemPrint" Click="PrintOnExecute" 
  Header="_Print..." InputGestureText="Ctrl+P" Icon="{StaticResource iconPrint}" />
                <Separator />
                <MenuItem Name="itemExit" Click="ExitOnExecute" Header="E_xit" />
            </MenuItem>
            <MenuItem Header="_Edit">
                <MenuItem Name="itemUndo" Click="UndoOnExecute"   
Header="_Undo" InputGestureText="Ctrl+Z" Icon="{StaticResource iconUndo}" />
                <MenuItem Name="itemRedo" Click="RedoOnExecute" 
Header="_Redo" InputGestureText="Ctrl+Y" Icon="{StaticResource iconRedo}" />
                <Separator></Separator>
                <MenuItem Name="itemCut" Click="CutOnExecute" 
Header="Cu_t" InputGestureText="Ctrl+X" Icon="{StaticResource iconCut}" />
                <MenuItem Name="itemCopy" Click="CopyOnExecute" 
Header="_Copy" InputGestureText="Ctrl+C" Icon="{StaticResource iconCopy}" />
                <MenuItem Name="itemPaste" Click="PasteOnExecute" 
Header="_Paste" InputGestureText="Ctrl+V" Icon="{StaticResource iconPaste}" />
                <MenuItem Name="itemDelete" Click="DeleteOnExecute" 
Header="De_lete" InputGestureText="Del" Icon="{StaticResource iconDelete}" />
                <Separator></Separator>
                <MenuItem Name="itemFind" Click="FindOnExecute" 
Header="_Find..." InputGestureText="Ctrl+F" Icon="{StaticResource iconFind}" />
                <MenuItem Name="itemFindNext" Click="FindNextOnExecute" 
Header="Find _Next" InputGestureText="F3" />
                <MenuItem Name="itemReplace" Click="ReplaceOnExecute" 
Header="_Replace..." InputGestureText="Ctrl+H" />
                <MenuItem Name="itemGoTo" Click="GoToOnExecute" 
Header="_Go To..." InputGestureText="Ctrl+G" />
                <Separator></Separator>
                <MenuItem Name="itemSelectAll" Click="SelectAllOnExecute" 
Header="Select _All" InputGestureText="Ctrl+A" />
            </MenuItem>
            <MenuItem Header="F_ormat">
                <MenuItem Name="itemFont" Click="FontOnExecute" 
Header="_Font..." Icon="{StaticResource iconFont}" />
                <Separator />
                <MenuItem Name="itemWordWrap" Click="WordWrapOnExecute" 
Header="_Word Wrap" IsCheckable="True" IsChecked="True" InputGestureText="Ctrl+W" />
            </MenuItem>
            <MenuItem Header="_Help">
                <MenuItem Name="itemAbout" Click="AboutOnExecute" 
Header="_About" />
            </MenuItem>
        </Menu>
        
        <!-- Панель инструментов -->
        <ToolBarTray DockPanel.Dock="Top">
            <ToolBar>
                <Button Name="btnNew" Click="NewOnExecute" Width="23" Content="{StaticResource iconNew}" />                 
<Button Name="btnOpen" Click="OpenOnExecute" Width="23" Content="{StaticResource iconOpen}" />
                <Button Name="btnSave" Click="SaveOnExecute" Width="23" Content="{StaticResource iconSave}" />
            </ToolBar>
            <ToolBar>
                <Button Name="btnUndo" Click="UndoOnExecute" Width="23" Content="{StaticResource iconUndo}" />
                <Button Name="btnRedo" Click="RedoOnExecute" Width="23" Content="{StaticResource iconRedo}" />
                <Separator />
                <Button Name="btnCut" Click="CutOnExecute" Width="23" Content="{StaticResource iconCut}" />
                <Button Name="btnCopy" Click="CopyOnExecute" Width="23" Content="{StaticResource iconCopy}" />
                <Button Name="btnPaste" Click="PasteOnExecute" Width="23" Content="{StaticResource iconPaste}" />
                <Button Name="btnDelete" Click="DeleteOnExecute" Width="23" Content="{StaticResource iconDelete}" />
            </ToolBar>
            <ToolBar Header="Find:">
                <TextBox Width="100" />
                <Button Name="btnFind" Click="FindOnExecute" Width="23" Content="{StaticResource iconFind}" />
            </ToolBar>
        </ToolBarTray>
        
        <!-- Строка состояния -->
        <StatusBar DockPanel.Dock="Bottom" Height="32" Name="statusBar">
            <Label>Simulator Application is Loading</Label>
            <Separator />
            <ProgressBar Height="20" Width="100" IsIndeterminate="True" />
        </StatusBar>
        
        <!-- Многострочное текстовое поле редактирования -->
        <TextBox TextWrapping="Wrap"
                 AcceptsReturn="True" 
                 AcceptsTab="True"
                 VerticalScrollBarVisibility="Auto"
                 Name="txtBox1"
                 >
            <TextBox.ContextMenu>
                <ContextMenu Width="100">
                    <MenuItem Name="contextCut" Click="CutOnExecute" 
Header="Cu_t" Icon="{StaticResource iconCut}" />
                    <MenuItem Name="contextCopy" Click="CopyOnExecute" 
Header="_Copy" Icon="{StaticResource iconCopy}" />
                    <MenuItem Name="contextPaste" Click="PasteOnExecute" 
Header="_Paste" Icon="{StaticResource iconPaste}" />
                    <MenuItem Name="contextDelete" Click="DeleteOnExecute" 
Header="De_lete" Icon="{StaticResource iconDelete}" />
                </ContextMenu>
            </TextBox.ContextMenu>
        </TextBox>
    </DockPanel>
</Window>

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

  • Запустите приложение для проверки отсутствия синтаксических ошибок

Следующим шагом следует наполнить созданные заготовки обработчиков практическим кодом в соответствии с логикой работы приложения. Поскольку наша задача состоит не в создании полнофункционального приложения целиком, а в иллюстрации технологии его создания с применением механизма команд, реализуем намеченные задачи частично. Некоторые функции, ввиду их большого объема и сложности кодирования, мы только обозначим выдачей соответствующих диалоговых окон или сообщений. Управление доступностью элементов-источников задач пользовательского интерфейса пока отложим 'на потом' (или навсегда!).

Реализация обработчиков раздела меню File

  • Выделите корень проекта Notepad1 и командой Project/Add Reference подключите сборки System.Windows.Forms.dll и System.Drawing.dll
  • Модифицируйте код файла File.cs следующим образом
using System;
using System.Collections.Generic;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
    
// Для сборок: System.Windows.Forms.dll, System.Drawing.dll
using sdp = System.Drawing.Printing;// Псевдоним пространства имен 
using swf = System.Windows.Forms;   // Псевдоним пространства имен
using PageSetupDialog = System.Windows.Forms.PageSetupDialog;// Псевдоним класса
    
namespace Notepad1
{
    partial class Window1
    {
        //------------------------------------------------------
        //
        //  Обработчики источников задач File
        //
        //------------------------------------------------------
    
        private void NewOnExecute(object sender, RoutedEventArgs e)
        {
            // Пользователь передумал или была ошибка записи изменений
            if (!CheckModifiedAndSaveIt())
                return;
    
            // Изменений нет или они успешно сохранены
            //txtBox1.Text = String.Empty;  // Вариант I
            //txtBox1.Text = "";            // Вариант II
            txtBox1.Clear();                // Вариант III
            strLoadedFile = null;
            IsModified = false;
            UpdateTitle();
            txtBox1.Focus();
        }
    
        private void OpenOnExecute(object sender, RoutedEventArgs e)
        {
            if(DisplayOpenDialog())
                txtBox1.CaretIndex = txtBox1.Text.Length;// Курсор в конец
            txtBox1.Focus();// Передача фокуса
        }
    
        private void SaveOnExecute(object sender, RoutedEventArgs e)
        {
            if (String.IsNullOrEmpty(strLoadedFile))
                DisplaySaveDialog(String.Empty);
            else
                SaveFile(strLoadedFile);
            txtBox1.Focus();
        }
    
        private void SaveAsOnExecute(object sender, RoutedEventArgs e)
        {
            DisplaySaveDialog(strLoadedFile);
            txtBox1.Focus();
        }
    
        private void PageSetupOnExecute(object sender, RoutedEventArgs e)
        {
            // Ограничемся только показом окна Windows Forms
            PageSetupDialog dlg = new PageSetupDialog();
    
            // Без настроек не работает. Зададим хотя бы по умолчанию
            dlg.PageSettings = new sdp.PageSettings();
            dlg.PrinterSettings = new sdp.PrinterSettings();
    
            dlg.ShowDialog();
            txtBox1.Focus();
        }
    
        private void PrintPreviewOnExecute(object sender, RoutedEventArgs e)
        {
            sdp.PrintDocument document = new sdp.PrintDocument();
            document.DocumentName = strLoadedFile;
    
            swf.PrintPreviewDialog dlg = new swf.PrintPreviewDialog();
            dlg.Document = document;
            dlg.UseAntiAlias = true;// Включить сглаживание
    
            dlg.ShowDialog();
            txtBox1.Focus();
        }
    
        private void PrintOnExecute(object sender, RoutedEventArgs e)
        {
            sdp.PrintDocument document = new sdp.PrintDocument();
            document.DocumentName = strLoadedFile;
    
            swf.PrintDialog dlg = new swf.PrintDialog();
            dlg.Document = document;
            dlg.ShowDialog();
            txtBox1.Focus();
        }
    
        private void ExitOnExecute(object sender, RoutedEventArgs e)
        {
            if (!CheckModifiedAndSaveIt())
                return; // Пользователь передумал выходить
            Close();
        }
    }
}

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

  • Запустите приложение, испытайте правильность функционирования обработчиков, изучите их код

Обработка системной кнопки

Пока в приложение закрался один существенный недостаток. Когда документ содержит несохраненные изменения и пользователь завершает работу приложения по нашей команде Exit, то все в порядке - приложение извещает о необходимости их сохранить. Но когда окно закрывается по системной кнопке, то извещение отсутствует. Исправим это, для чего воспользуемся событием Closing. В отличии от Closing событие Closed возбудается, когда отменить закрытие окна уже невозможно, а можно только что-то доделать.

  • Присоедините к открывающему дескриптору окна Window1 в файле Window1.xaml обработчик события Closing, которое будет возбуждаться при попытке закрытия окна любым способом
<Window x:Class="Notepad1.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        
    Title="Window1: Управление состоянием источников команд"
    Width="500" Height="375"
    MinWidth="500" MinHeight="375"
    WindowStartupLocation="CenterScreen"
    ResizeMode="CanResizeWithGrip"
    Loaded="Window_Loaded"
    Icon="Notepad.ico"
    Closing="Window_Closing"
        >
  • Заполните обработчик события Closing следующим кодом
private void Window_Closing(object sender, 
            System.ComponentModel.CancelEventArgs e)
        {
            if (!CheckModifiedAndSaveIt())
            {
                e.Cancel = true;
                return; // Пользователь передумал выходить
            }
        }
  • Запустите приложение - теперь сообщение о несохраненных изменениях выводится и при попытке закрытия окна системной кнопкой

Обратите внимание, что при закрытии окна через наше меню задачей Exit на кнопку отказа от сохранения надо щелкать 2 раза. Это связано с тем, что в обработчике события Closing проверка повторяется. Значит при выходе через меню проверку в обработчике события Closing нужно блокировать с помощью флага.

  • Добавьте в класс Window1 поле-флаг _IsExitItem и модифицируйте соответствующим образом обработчики
bool _IsExitItem = false;
        private void ExitOnExecute(object sender, RoutedEventArgs e)
        {
            if (!CheckModifiedAndSaveIt())
                return; // Пользователь передумал выходить
    
            _IsExitItem = true;
            Close();
        }
private void Window_Closing(object sender, 
            System.ComponentModel.CancelEventArgs e)
        {
            /*
            // Эта проверка была бы надежнее
            if(_IsExitItem)
                return;
            */
            // !_IsExitItem должен в условии стоять первым
            if (!_IsExitItem && !CheckModifiedAndSaveIt())
            {
                e.Cancel = true;
                _IsExitItem = false;
                return; // Пользователь передумал выходить
            }
        }

В последнем обработчике есть один поучительный нюанс: если в условии проверку значения флага поставить последним, то код будет реагировать на наши нововведения. Это происходит потому, что логическое умножение проверяется компилятором слева направо до первого ложного значения. В нашем случае если флаг _IsExitItem==true, то функция CheckModifiedAndSaveIt() выполняться уже не будет. Такой код менее надежен, поскольку зависит от компилятора, да и мы (или сопровождающий программист) можем случайно переставить члены выражения местами. Поэтому лучше заменить этот код на более ясный, как показано в коментариях.

  • Запустите приложение и испытайте работу закрытия окна при несохраняемых изменениях
Алексей Бабушкин
Алексей Бабушкин

При выполнении в лабораторной работе упражнения №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" или один из зависимых от них компонентов. Не удается найти указанный файл.

Делаю все пунктуально. В чем может быть проблема?