Опубликован: 05.08.2010 | Уровень: специалист | Доступ: платный
Самостоятельная работа 12:

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

Прямой вызов команд

Команды WPF можно вызывать и напрямую, а необязательно присоединять к источникам. Правда, это будет уже извращение и сам механизм команд будет использоваться только частично. При таком подходе придется реагировать на состояния источников и на возможность выполнения команды самостоятельным кодом. Но знать о существовании такой возможности программисту следует. Покажем это...

  • Добавьте к интерфейсу окна еще четыре кнопки так
<Window x:Class="BindingCommandsXAML.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="Window1: Декларативная привязка" Height="300" Width="300"
    Background="#FFD4D0C8"
    WindowStartupLocation="CenterScreen"
    >
    <Window.CommandBindings>
        <CommandBinding 
            Command="ApplicationCommands.Open" 
            Executed="OpenCommand_Executed">
        </CommandBinding>
        <CommandBinding 
            Command="ApplicationCommands.Save" 
            Executed="SaveCommand_Executed">
        </CommandBinding>
    </Window.CommandBindings>
    <StackPanel Margin="5">
        <Menu>
            <MenuItem Header="_File">
                <MenuItem Command="ApplicationCommands.Open" />
                <MenuItem Command="ApplicationCommands.Save" />
            </MenuItem>
        </Menu>
        <Button Margin="5" Padding="5" Focusable="False"
                Command="ApplicationCommands.Open" 
                Content="{x:Static ApplicationCommands.Open}" 
                />
        <Button Margin="5" Padding="5" Focusable="False"
                Command="ApplicationCommands.Save" 
                Content="{Binding RelativeSource={RelativeSource Self}, Path=Command.Text}" 
                />
        <Button Margin="5" Padding="5" Focusable="False"
                Content="DirectCommandsOpen" 
                Click="directCommandsOpen_Click"
                />
        <Button Margin="5" Padding="5" Focusable="False"
                Content="DirectCommandsSave" 
                Click="directCommandsSave_Click"
                />
        <Button Margin="5" Padding="5" Focusable="False"
                Content="DirectBindingsOpen" 
                Click="directBindingsOpen_Click"
                />
        <Button Margin="5" Padding="5" Focusable="False"
                Content="DirectBindingsSave" 
                Click="directBindingsSave_Click"
                />
    </StackPanel>
</Window>
  • Вызовите контекстное меню для записей события Click и командой Navigate to Event Handler создайте четыре обработчика в файле процедурного кода
  • Заполните обработчики следующим кодом прямого вызова команд
private void directCommandsOpen_Click(object sender, RoutedEventArgs e)
        {
            ApplicationCommands.Open.Execute(null, this);
        }
    
        private void directCommandsSave_Click(object sender, RoutedEventArgs e)
        {
            ApplicationCommands.Save.Execute(null, this);
        }
    
        private void directBindingsOpen_Click(object sender, RoutedEventArgs e)
        {
            this.CommandBindings[0].Command.Execute(null);
        }
    
        private void directBindingsSave_Click(object sender, RoutedEventArgs e)
        {
            this.CommandBindings[1].Command.Execute(null);
        }

В прямых вызовах двух первых обработчиков начальный параметр метода Execute() ожидает объект с дополнительной информацией, который будет передан на обработчик команды. Второй параметр принимает целевой элемент привязки команды с возможностями интерфейса IInputElement, который будет прослушивать команду и вызывать обработчик. В нашем случае это объект окна. Два следующих обработчика используют вызовы команд из коллекции прослушивающего элемента (окна), привязанные к нему в разметке.

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

Упражнение 5. Привязка команд в процедурном коде

Частично повторим предыдущее упражнение, но привязку команд выполним в процедурном коде.

  • Добавьте к решению командой File/Add/New Project новый проект с именем BindingCommandsCode и назначьте его стартовым

  • Откорректируйте разметку окна Window1 так
<Window x:Class="BindingCommandsCode.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="Window1: Кодовая привязка" Height="300" Width="300"
    Background="#FFD4D0C8"
    WindowStartupLocation="CenterScreen"
    >
    <StackPanel Margin="5">
        <Menu>
            <MenuItem Header="_File">
                <MenuItem Command="ApplicationCommands.Open" />
                <MenuItem Command="ApplicationCommands.Save" />
            </MenuItem>
        </Menu>
        <Button Margin="5" Padding="5" Focusable="False"
                Command="ApplicationCommands.Open" 
                Content="Open" 
                />
        <Button Margin="5" Padding="5" Focusable="False"
                Command="ApplicationCommands.Save" 
                Content="Save" 
                />
    </StackPanel>
</Window>

Заметьте, что в этой разметке отсутствует как секция привязки команд, так и ссылки на обработчики команд. Все это мы выполним в процедурной части окна.

  • Внесите следующие изменения в класс Window1 файла процедурного кода Window1.xaml.cs
public partial class Window1 : Window
    {
        public Window1()
        {
            InitializeComponent();
    
            // Регистрация обработчика
            this.Loaded += new RoutedEventHandler(Window1_Loaded);
        }
    
        void Window1_Loaded(object sender, RoutedEventArgs e)
        {
            // Привязка команд в коде
            CommandBinding binding = new CommandBinding();
            binding.Command = ApplicationCommands.Open;
            binding.Executed += 
                new ExecutedRoutedEventHandler(OpenCommand_Executed);
            this.CommandBindings.Add(binding);
    
            binding = new CommandBinding();
            binding.Command = ApplicationCommands.Save;
            binding.Executed += 
                new ExecutedRoutedEventHandler(SaveCommand_Executed);
            this.CommandBindings.Add(binding);
        }
    
        void OpenCommand_Executed(object sender, ExecutedRoutedEventArgs e)
        {
            MessageBox.Show("Выполнена команда Open");
        }
    
        void SaveCommand_Executed(object sender, ExecutedRoutedEventArgs e)
        {
            MessageBox.Show("Выполнена команда Save");
        }
    }
  • Запустите приложение и убедитесь в работоспособности механизма команд WPF


Жесты как источники команд

Комбинации клавиш, предоставляющие прямой доступ к выполнению команд меню, ранее называли акселераторами (ускорителями), теперь - жестами. Жесты можно хранить в самой команде в коллекции InputGestures, а можно хранить в прослушивающем элементе в коллекции InputBindings наряду с командой. Кроме интерфейсных элементов жесты могут служить еще одним источником команд. Команда, имеющая жесты в своей коллекции, может вообще не быть присоединенной ни к одному из визуальных элементов, а возбуждаться только жестами.

Добавление жестов в команду

Жесты представленны абстрактным классом System.Windows.Input. InputGesture. Этот класс наследует двум типам жестов: клавиатурных и мыши, которые представлены объектами KeyGesture и MouseGesture соответственно. Команды, в свою очередь, имеют коллекцию жестов InputGestures, которую заранее можно наполнить объектами нужных жестов клавиатуры и мыши. Комбинации клавиш и кнопок мыши проще всего добавлять в объект жеста через его конструктор при создании экземпляра по схеме ( Key, ModifierKeys ) и ( MouseAction, ModifierKeys ).

В следующем примере показано, как в команду можно добавить жесты, которые будут ее запускать

// Клавиатурный жест Control+F
InputGesture gesture = new KeyGesture(Key.F, ModifierKeys.Control, "Ctrl+F");
ApplicationCommands.Find.InputGestures.Add(gesture);
    
// Комбинированный жест Control+LeftClick
gesture = new MouseGesture(MouseAction.LeftClick, ModifierKeys.Control);
ApplicationCommands.Find.InputGestures.Add(gesture);
    
// Клавиатурный жест Control+Q
KeyGesture keyGesture = new KeyGesture(Key.Q, ModifierKeys.Control, "Ctrl+Q");
ApplicationCommands.Find.InputGestures.Add(keyGesture);
    
// Комбинированный жест Alt+LeftClick
MouseGesture mouseGesture = new MouseGesture();
mouseGesture.MouseAction = MouseAction.LeftClick;
mouseGesture.Modifiers = ModifierKeys.Alt;
ApplicationCommands.Find.InputGestures.Add(mouseGesture);
//gesture = new MouseGesture(MouseAction.LeftClick, ModifierKeys.Alt);
//ApplicationCommands.Find.InputGestures.Add(gesture);

Теперь команда Find будет запускаться щелчком на кнопке, щелчком левой кнопки мыши с нажатой клавишей Ctrl, комбинациями клавиш Ctrl+F или Ctrl+Q.

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

  • Добавьте в обработчик события Loaded класса окна Window1 следующий код, модифицирующий жесты библиотечных команд Open и Save
public partial class Window1 : Window
    {
        public Window1()
        {
            InitializeComponent();
    
            // Регистрация обработчика
            this.Loaded += new RoutedEventHandler(Window1_Loaded);
        }
    
        void Window1_Loaded(object sender, RoutedEventArgs e)
        {
            // Привязка команд в коде
            CommandBinding binding = new CommandBinding();
            binding.Command = ApplicationCommands.Open;
            binding.Executed += 
                new ExecutedRoutedEventHandler(OpenCommand_Executed);
            this.CommandBindings.Add(binding);
    
            binding = new CommandBinding();
            binding.Command = ApplicationCommands.Save;
            binding.Executed += 
                new ExecutedRoutedEventHandler(SaveCommand_Executed);
            this.CommandBindings.Add(binding);
    
            // Очистка коллекций прежних жестов команд
            ApplicationCommands.Open.InputGestures.Clear();
            ApplicationCommands.Save.InputGestures.Clear();
    
            // Добавление новых жестов клавиатуры Alt+O и Alt+S
            InputGesture key = new KeyGesture(Key.O, ModifierKeys.Alt, "Alt+O");
            ApplicationCommands.Open.InputGestures.Add(key);
            //
            KeyGesture keyGesture = new KeyGesture(Key.S, ModifierKeys.Alt, "Alt+S");
            ApplicationCommands.Save.InputGestures.Add(keyGesture);
    
            // Добавление новых жестов мыши Ctrl+LeftClick и Ctrl+RightClick
            InputGesture mouse = new MouseGesture(MouseAction.LeftClick, ModifierKeys.Control);
            ApplicationCommands.Open.InputGestures.Add(mouse);
            //
            MouseGesture mouseGesture = new MouseGesture();
            mouseGesture.MouseAction = MouseAction.RightClick;
            mouseGesture.Modifiers = ModifierKeys.Control;
            ApplicationCommands.Save.InputGestures.Add(mouseGesture);
        }
    
        void OpenCommand_Executed(object sender, ExecutedRoutedEventArgs e)
        {
            MessageBox.Show("Выполнена команда Open");
        }
    
        void SaveCommand_Executed(object sender, ExecutedRoutedEventArgs e)
        {
            MessageBox.Show("Выполнена команда Save");
        }
    }

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

  • Запустите приложение и убедитесь, что теперь прежние клавиатурные комбинации Ctrl+O и Ctrl+S не работают, вместо них программа реагирует на жесты клавиатуры Alt+O и Alt+S, а также выполняются комбинированные жесты с участием мыши Ctrl+LeftClick и Ctrl+RightClick

В нашей программе после добавления жестов появился существенный недостаток - сами жесты мы поменяли, но в меню все-таки остались старые маркеры команд Ctrl+O и Ctrl+S. Хоть мы и пытались в конструкторе KeyGesture() определить в последнем параметре displayString новые маркеры для жестов в меню, но все осталось по прежнему. Дело здесь в том, что объект меню создается и инициализируется в конструкторе класса по объекту уже присоединенной к источнику команды. И это происходит раньше, чем мы в коде начинаем модифицировать саму команду.

Чтобы это исправить, можно воспользоваться несколькими способами:

Способ 1
  • Определитете в разметке в элементах меню свойство InputGestureText следующим образом
<Menu>
            <MenuItem Header="_File">
                <MenuItem Command="ApplicationCommands.Open" 
                          InputGestureText="Alt+O" />
                <MenuItem Command="ApplicationCommands.Save" 
                          InputGestureText="Alt+S" />
            </MenuItem>
        </Menu>
  • Запустите приложение и убедитесь, что теперь маркеры команд меню изменились
Способ 2

Вместо разметки изменить свойство InputGestureText можно в процедурном коде, но для этого элементам меню нужно присвоить имена.

  • Удалите в разметке из дескрипторов элементов меню параметры Command и InputGestureText, а добавьте вместо них имена элементов, как показано ниже
<Menu>
            <MenuItem Header="_File">
                <MenuItem Name="openMenu" />
                <MenuItem Name="saveMenu" />
            </MenuItem>
        </Menu>
  • Добавьте в самый конец обработчика события Loaded после кода добавления жестов следующую пару строк динамического присоединения модифицированных команд к меню-источнику
void Window1_Loaded(object sender, RoutedEventArgs e)
        {
            // Привязка команд в коде
            CommandBinding binding = new CommandBinding();
            binding.Command = ApplicationCommands.Open;
            binding.Executed += 
                new ExecutedRoutedEventHandler(OpenCommand_Executed);
            this.CommandBindings.Add(binding);
    
            binding = new CommandBinding();
            binding.Command = ApplicationCommands.Save;
            binding.Executed += 
                new ExecutedRoutedEventHandler(SaveCommand_Executed);
            this.CommandBindings.Add(binding);
    
            // Очистка коллекций прежних жестов команд
            ApplicationCommands.Open.InputGestures.Clear();
            ApplicationCommands.Save.InputGestures.Clear();
    
            // Добавление новых жестов клавиатуры Alt+O и Alt+S
            InputGesture key = new KeyGesture(Key.O, ModifierKeys.Alt, "Alt+O");
            ApplicationCommands.Open.InputGestures.Add(key);
            //
            KeyGesture keyGesture = new KeyGesture(Key.S, ModifierKeys.Alt, "Alt+S");
            ApplicationCommands.Save.InputGestures.Add(keyGesture);
    
            // Добавление новых жестов мыши Ctrl+LeftClick и Ctrl+RightClick
            InputGesture mouse = new MouseGesture(MouseAction.LeftClick, ModifierKeys.Control);
            ApplicationCommands.Open.InputGestures.Add(mouse);
            //
            MouseGesture mouseGesture = new MouseGesture();
            mouseGesture.MouseAction = MouseAction.RightClick;
            mouseGesture.Modifiers = ModifierKeys.Control;
            ApplicationCommands.Save.InputGestures.Add(mouseGesture);
    
            // Присоединяем модифицированные команды к меню-источнику
            openMenu.Command = ApplicationCommands.Open;
            saveMenu.Command = ApplicationCommands.Save;
        }
  • Запустите приложение и убедитесь, что теперь маркеры команд меню изменились в соответствии с введенными нами при формировании новых жестов
Способ 3
  • Разметку меню сделайте такой
<Menu>
            <MenuItem Header="_File">
                <MenuItem Name="openMenu" 
                          Command="ApplicationCommands.Open" />
                <MenuItem Name="saveMenu" 
                          Command="ApplicationCommands.Save" />
            </MenuItem>
        </Menu>
  • Замените в обработчике Window1_Loaded () только что добавленный код на новый
// Присоединяем модифицированные команды к меню-источнику
            //openMenu.Command = ApplicationCommands.Open;
            //saveMenu.Command = ApplicationCommands.Save;
            openMenu.InputGestureText = "Alt+O";
            saveMenu.InputGestureText = "Alt+S";
  • Запустите приложение и убедитесь, что маркеры команд меню стали правильными

Добавление жестов в прослушивающий элемент

Есть еще один способ добавления жестов, о котором стоит упомянуть. Ранее обсуждалось, что все пользовательские элементы WPF наследуют от UIElement или ContentElement, а эти классы, в свою очередь, имеют в качестве свойства коллекцию InputBindings типа InputBindingCollection. Коллекция может быть заполнена классами KeyBinding или MouseBinding, каждый из которых связывает жест клавиатуры (представленный объектом KeyGesture ) или мыши (представленный объектом MouseGesture ) с командой. Оба класса наследуют тип InputBinding.

Например, привязать жесты с командой к объекту окна можно одним из следующих способов

<Window x:Class="Tmp.window"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Height="300" Width="300">
    <Window.InputBindings>
        <KeyBinding Key="F"
              Modifiers="Control" 
              Command="ApplicationCommands.Find" />
        <MouseBinding MouseAction="Control+LeftClick"
              Command="ApplicationCommands.Find" />
        <KeyBinding Key="Q"
              Modifiers="Control" 
              Command="ApplicationCommands.Find" />
        <MouseBinding Gesture="Alt+LeftClick"
              Command="ApplicationCommands.Find" />
    </Window.InputBindings>
    <Grid>
        <Button Command="ApplicationCommands.Find"
                Height="23"
                Width="75"
                >
            Поиск
        </Button>
    </Grid>
</Window>

В классе MouseBinding свойство Modifiers доступно только для чтения, поэтому жест для мыши нужно вводить в свойство MouseAction сразу целиком, как показано в только что приведенном примере.

// Клавиатурный жест Control+F
KeyGesture keyGesture = new KeyGesture(Key.F, ModifierKeys.Control);
KeyBinding keyBinding = new KeyBinding(ApplicationCommands.Find, keyGesture);
this.InputBindings.Add(keyBinding);
    
// Комбинированный жест Control+LeftClick
MouseGesture mouseGesture = new MouseGesture(MouseAction.LeftClick, ModifierKeys.Control);
MouseBinding mouseBinding = new MouseBinding(ApplicationCommands.Find, mouseGesture);
this.InputBindings.Add(mouseBinding);
    
// Клавиатурный жест Control+Q
InputGesture gesture = new KeyGesture(Key.Q, ModifierKeys.Control);
ICommand command = ApplicationCommands.Find;
keyBinding = new KeyBinding(command, (KeyGesture)gesture);
this.InputBindings.Add(keyBinding);
    
// Комбинированный жест Alt+LeftClick
gesture = new MouseGesture();
((MouseGesture)gesture).MouseAction = MouseAction.LeftClick;
((MouseGesture)gesture).Modifiers = ModifierKeys.Alt;
mouseBinding = new MouseBinding();
mouseBinding.Command = command;
mouseBinding.Gesture = gesture;
this.InputBindings.Add(mouseBinding);

Упражнение 6. Разработка простого блокнота без механизма команд

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

  • Командой Add/New Project добавьте к решению новый проект WPF Application с именем Notepad1 и назначьте его стартовым

Основную часть интерфейса приложения, как и положено, мы создадим декларативно в разметке XAML, а управление - в части процедурного кода C#.

  • Отредактируйте интерфейсную часть окна следующим образом
<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"
        >
    <DockPanel>
    </DockPanel>
</Window>

Создание главного меню

  • Добавьте в дескриптор <DockPanel> </DockPanel> разметку создания меню
<DockPanel LastChildFill="True">
        <!-- Меню -->
        <Menu DockPanel.Dock="Top">
            <MenuItem Header="_File">
                <MenuItem Header="_New" InputGestureText="Ctrl+N">
                </MenuItem>
                <MenuItem Header="_Open..." InputGestureText="Ctrl+O" />
                <MenuItem Header="_Save" InputGestureText="Ctrl+S" /> 
                <MenuItem Header="Save _As..." />
                <Separator />
                <MenuItem Header="Page Set_up..." />
                <MenuItem Header="P_rint Preview" InputGestureText="Ctrl+F2" />
                <MenuItem Header="_Print..." InputGestureText="Ctrl+P" />
                <Separator />
                <MenuItem Header="E_xit" />
            </MenuItem>
            <MenuItem Header="_Edit">
                <MenuItem Header="_Undo" InputGestureText="Ctrl+Z" />
                <MenuItem Header="_Redo" InputGestureText="Ctrl+Y" />
                <Separator></Separator>
                <MenuItem Header="Cu_t" InputGestureText="Ctrl+X" />
                <MenuItem Header="_Copy" InputGestureText="Ctrl+C" />
                <MenuItem Header="_Paste" InputGestureText="Ctrl+V" />
                <MenuItem Header="De_lete" InputGestureText="Del" />
                <Separator></Separator>
                <MenuItem Header="_Find..." InputGestureText="Ctrl+F" />
                <MenuItem Header="Find _Next" InputGestureText="F3" />
                <MenuItem Header="_Replace..." InputGestureText="Ctrl+H" />
                <MenuItem Header="_Go To..." InputGestureText="Ctrl+G" />
                <Separator></Separator>
                <MenuItem Header="Select _All" InputGestureText="Ctrl+A" />
            </MenuItem>
            <MenuItem Header="F_ormat">
                <MenuItem Header="_Font..." />
                <Separator />
                <MenuItem Header="_Word Wrap" IsCheckable="True" IsChecked="True" InputGestureText="Ctrl+W" />
            </MenuItem>
            <MenuItem Header="_Help">
                <MenuItem Header="_About" />
            </MenuItem>
        </Menu>
    </DockPanel>

Знаки подчеркивания мы использовали для того, чтобы выделить символы, по которым пользователь может раскрывать меню и запускать команды после нажатия клавиш Alt или F10. Отмеченные символы должны быть уникальными как на горизонтальной линейке на уровне разделов, так и в рамках одного раздела меню. Каждому пункту присвоено название команды и клавиатурного жеста. В команде Word Wrap свойство IsCheckable обеспечивает самопереключаемость флажка, а его начальное состояние задается свойством IsChecked.

Алексей Бабушкин
Алексей Бабушкин

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

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

Dmitriy Ivanchenko
Dmitriy Ivanchenko
Украина, Кировоград, Виктория-П, 2011
Татьяна Ковалюк
Татьяна Ковалюк
Украина, Киев, Киевский политехнический институт, 1974