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

Графика в WPF

  • Командой Project/Add Reference добавьте к проекту ссылку на библиотеку System.Windows.Forms.dll, в которой находится перечисление Keys кодов клавиш


  • Запустите проект FullScreen и испытайте построенную нами имитацию полноэкранного режима

Оконный и полноэкранный (уменьшенный масштаб) режимы будут выглядеть так



Главное меню мы создали для примера, чтобы показать, что его можно отключать в полноэкранном режиме. Переключение режимов выполняется вызовом контекстного меню по щелчку правой кнопки мыши на окне приложения. В полноэкранном режиме отключаются нежелательные для нас системные клавиши, которые могли бы привести к сбою режима:

  • CTRL + ESC: открытие меню Пуск.
  • Клавиша WIN: открытие меню Пуск.
  • ALT + ESC: показать панель задач и переключать развернутые окна.
  • ALT + TAB: переключение между программами.

В оконном режиме функциональность системных клавиш восстанавливается.

Для полного сходства можно было бы отключить мышь, но тогда все управление окном нужно будет возложить на клавиатуру. В нашем приложении мы для закрытия окна использовали клавишу Esc. Закрыть окно можно и системной комбинацией Alt+F4, которую мы не отключали. Осталась активной комбинация Alt+Space, вызывающая системное меню приложения.

Мы использовали в классе HookSystemKeys перечисление System.Windows.Forms.Keys библиотечной сборки System.Windows.Forms, а в обработчике Window_KeyDown - перечисление System.Windows.Input.Key библиотечной сборки WindowsBase.dll. Это разные перечисления и их нельзя путать.

  • Попробуйте разобраться с кодом

Упражнение 3. Применение объекта ImageDrawing

Объект ImageDrawing принимает графические данные в виде битовой карты (точечного рисунка). Для создания изображения необходимо создать экземпляр ImageDrawing и установить значения его свойств ImageDrawing.ImageSource и ImageDrawing.Rect. Свойство ImageDrawing.ImageSource задает изображение для рисования, а свойство ImageDrawing.Rect задает положение и размер каждого изображения.

Если нужно отобразить один рисунок, то его сразу можно присоединить к объекту отображения Image, как мы это делали в предыдущем упражнении. Если же требуется нарисовать несколько рисунков сразу, то необходимо организовать конвейер формирования рисунков. Вначале создаются отдельные слои с помощью ImageDrawing, затем они передаются в накопитель DrawingGroup, где располагаются в Z -последовательности в порядке их добавления. В таком порядке каждый новый слой будет перекрывать все предыдущие и располагаться ближе к пользователю. Затем этот слоеный объект передается в рисовальщик DrawingImage, который и присоединяется, в конечном итоге, к элементу отображения Image.

Объект DrawingGroup как составной объект рисования, может принимать не только точечные рисунки. В первом упражнении мы использовали его для накопления векторных рисунков геометрии, порожденных объектом GeometryDrawing. Он также способен принимать текстовые данные от GlyphRunDrawing, или медийные данные звука и видео от объекта VideoDrawing. Объект DrawingGroup является единственным типом базового объекта Drawing, который позволяет определять свою собственную область отсечения. Но об этом чуть позже, а сейчас приступим к рассмотрению объекта ImageDrawing.

Класс ImageDrawing легко спутать с важным классом DrawingImage, названия которых так похожи. Но это нас не должно путать, если мы представим изготовление готового рисунка как последовательность операций на конвейере. Это будет выглядеть примерно так:

GeometryDrawing -> DrawingGroup -> DrawingImage -> Image
ImageDrawing
GlyphRunDrawing
VideoDrawing
  • Добавьте к решению проект типа WPF Application с именем WpfApp3 и назначьте его стартовым
  • Добавьте к проекту командой Project/New Folder папку с именем Images
  • В панели Solution Explorer вызовите контекстное меню для папки Images и скопируйте в нее командой Add/Existing Item из прилагаемого каталога Source пять файлов с рисунками (не забудьте изменить фильтр диалогового окна Add Existing Item на All Files ):
    • market 031.jpg
    • market 032.jpg
    • market 034.jpg
    • market 039.jpg
    • market 040.jpg
  • В панели Solution Explorer выделите все пять рисунков одновременно и в панели Properties установите для них свойства
    • Build Action=Content
    • Copy to Output Directory=Copy if newer

В этом упражнении мы рассмотрим два способа использования объекта ImageDrawing - с помощью разметки на XAML и с помощью кода C#. Оба способа работают совершенно одинаково и для того, чтобы это подчеркнуть, мы с помощью них реализуем одну и ту же задачу. Попутно рассмотрим решение некоторых мелких вопросов, таких, как предотвращение повторного запуска созданного окна приложения, назначение главного окна и формирование всплывающей подсказки.

Вначале создадим главное окно приложения Window1, в котором примененим объект ImageDrawing в кодовой части Window1.xaml.cs. Затем построим дочернее окно Window2, в котором используем объект ImageDrawing в дискрипторной части Window2.xaml.

  • Заполните файл разметки Window1.xaml следующим дескрипторным кодом
<Window x:Class="WpfApp3.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="Главное окно Window1: Работа объекта ImageDrawing через процедурный код"
    Loaded="Window_Loaded"
    MouseDoubleClick="Show_Window2"
    SizeToContent="WidthAndHeight"
    ResizeMode="NoResize">
    <Window.ContextMenu>
        <ContextMenu>
            <MenuItem Header="Создать дочернее окно" Click="Create_Window2" />
        </ContextMenu>
    </Window.ContextMenu>
</Window>

Эта разметка настраивает окно и создает контекстное меню с одним пунктом. Вызов дочернего окна, которое будет иллюстрировать разметочный способ применения объекта ImageDrawing, предусмотрим в обработчиках событий MouseDoubleClick окна и Click контекстного меню. Такое дублирование одной и той же задачи создания дочернего окна выбрано потому, чтобы испытать разные способы предотвращения повторного запуска уже существующего окна.

Событие Loaded срабатывает после загрузки окна в оперативную память, поэтому в его обработчик удобно поместить код создания объектов содержимого окна. Атрибуты SizeToContent и ResizeMode дескриптора <Window> делают окно подстраиваемым под содержимое (при первом появлении) и неизменяемое в размерах пользователем, соответственно. Контекстное меню прикрепляется к объекту окна и будет вызываться в любой его точке, кроме заголовка.

  • Щелкните правой кнопкой мыши на каждом из событий Loaded, MouseDoubleClick, Click и выполните команду Navigate to Event Handler, чтобы создать в кодовой части обработчики с уже заготовленными именами
  • В файле Window1.xaml.cs заполните обработчик события Loaded следующим кодом
private void Window_Loaded(object sender, RoutedEventArgs e)
{
    // Размеры всех рисунков одинаковы
    const int WIDTH = 348, HEIGHT = 232;

    // Создаем накопитель рисунков DrawingGroup 
    DrawingGroup drawingGroup = new DrawingGroup();

    // Левый верхний 
    ImageDrawing pict1 = new ImageDrawing();
    pict1.Rect = new Rect(0, 0, WIDTH, HEIGHT);
    pict1.ImageSource = new BitmapImage(
        new Uri(@"Images\market 040.jpg", UriKind.Relative));
    drawingGroup.Children.Add(pict1);

    // Правый верхний 
    ImageDrawing pict2 = new ImageDrawing();
    pict2.Rect = new Rect(350, 0, WIDTH, HEIGHT);
    pict2.ImageSource = new BitmapImage(
        new Uri(@"Images\market 039.jpg", UriKind.Relative));
    drawingGroup.Children.Add(pict2);

    // Левый нижний
    ImageDrawing pict3 = new ImageDrawing();
    pict3.Rect = new Rect(0, 234, WIDTH, HEIGHT);
    pict3.ImageSource = new BitmapImage(
        new Uri(@"Images\market 034.jpg", UriKind.Relative));
    drawingGroup.Children.Add(pict3);

    // Правый нижний
    ImageDrawing pict4 = new ImageDrawing();
    pict4.Rect = new Rect(350, 234, WIDTH, HEIGHT);
    pict4.ImageSource = new BitmapImage(
        new Uri(@"Images\market 032.jpg", UriKind.Relative));
    drawingGroup.Children.Add(pict4);

    // Передать рисовальщику
    DrawingImage drawingImageSource = new DrawingImage(drawingGroup);

    // Заморозить DrawingImage для лучшей производительности
    drawingImageSource.Freeze();

    // Передать элементу отображения
    Image image = new Image();
    image.Stretch = Stretch.None;
    image.Source = drawingImageSource;

    // Контейнер Border для присоединения к содержимому окна
    Border border = new Border();
    border.Background = Brushes.White;
    border.BorderBrush = Brushes.White;
    border.BorderThickness = new Thickness(2);  // Толщина рамки
    border.Margin = new Thickness(10);          // Внешний отступ-поле
    border.Child = image;                       // Отдать родителю

    this.Background = Brushes.Blue;
    this.Content = border;
}
  • Дополните конструктор окна Window1 следующим кодом
public Window1()
{
    // Инициализация разметочной части
    InitializeComponent();

    // Корректировка заголовка окна
    this.Title += "=\"Так голодают буржуины!\"";

    // Всплывающая подсказка
    this.ToolTip = "Вызывайте дочернее окно\n"
        + "двойным щелчком мыши\n"
        + "или контекстным меню...";
}

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

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

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

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

Юрий Макушин
Юрий Макушин
Россия, Москва, РЭА им. Плеханова, 2004