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

Windows Forms и XNA 3.0

Потеря информации графическим устройством

Операционная система Windows является многозадачной системой, в которой одновременно могут выполняться несколько задач и делить между собой одни и те же ресурсы. Существует опасность, что настройки графического устройства, установленные нашим приложением XNA, в любой момент могут сбиться другим подобным приложением. Такая ситуация называется потерей устройства (Device Lost). Это легко проверить, если при запущенном приложении XNA попытаться изменить разрешение экрана - приложение завершится аварийно.

Отсюда возникает задача о восстановлении потерянной информации, рассмотрением которой мы сейчас и займемся. Информацию о состоянии, в котором в данный момент находится устройство, можно получить из свойства GraphicsDeviceStatus нашего объекта (экземпляра класса GraphicsDevice ), которое имеет одно из значений одноименного перечисления GraphicsDeviceStatus:

  • Normal - графическое устройство работает нормально
  • NotReset - графическое устройство потеряно, но может быть восстановлено методом Reset() объекта устройства
  • Lost - графическое устройство потеряно и пока не может быть восстановлено

При восстановимой потере устройства приложение выдает исключение Microsoft.Xna.Framework.Graphics.DeviceNotResetException, а при невосстановимой - Microsoft.Xna.Framework.Graphics.DeviceLostException. Если сгенерировано исключение DeviceNotResetException, то приложение должно восстановить устройство методом device.Reset() и снова перерисовать изображение путeм вызова метода Invalidate(), в противном случае просто не реагировать на событие Paint, требующее обновить экран.

  • На основе вышесказанного модифицируйте обработку события Paint следующим образом
bool closing = false;
        protected override void OnPaint(PaintEventArgs e)
        {

            if (closing) return;
    
            try     // Попытка
            {
                // Очищаем экран белым цветом
                device.Clear(Microsoft.Xna.Framework.Graphics.Color.WhiteSmoke);
    
                // Создаeм массив областей закраски, соответствующих коричневым клеткам
                Microsoft.Xna.Framework.Rectangle[] rects = new Microsoft.Xna.Framework.Rectangle[32];
                int k = 0; // Счетчик элементов массива 
                // Перебираем коричневые клетки шахматной доски 
                for (int j = 0; j < 8; j++) // Строки шахматной доски
                    for (int i = j % 2; i < 8; i += 2)  // Столбцы шахматной доски
                    {
                        // Заносим в массив параметры рисования очередной клетки
                        rects[k] = new Microsoft.Xna.Framework.Rectangle(
                            i * this.ClientSize.Width / 8,  // Отступ по горизонтали
                            j * this.ClientSize.Height / 8, // Отступ по вертикали
                            this.ClientSize.Width / 8,      // Ширина клетки
                            this.ClientSize.Height / 8);    // Высота клетки
    
                        k++;    // Увеличиваем счетчик
                    }
    
                // Закрашиваем все области из массива rects коричневым цветом
                device.Clear(ClearOptions.Target, Microsoft.Xna.Framework.
                    Graphics.Color.Brown, 0.0f, 0, rects);
    
                // Копируем задний буфер на экран
                device.Present();
    
                base.OnPaint(e);
            }
            // Откаты
            catch (DeviceNotResetException) // Устройство можно восстановить
            {
                device.Reset(presentParams);
                this.Invalidate();
            }
            catch (DeviceLostException)     // Устройство нельзя восстановить
            {
                closing = true;     // Больше не выполнять OnPaint()
                string title = "Сбой графического устройства";
                string message = "Работа программы будет завершена.\n"
                               + "Закройте все ненужные программы\n"
                               + "и повторите запуск этого приложения";
                MessageBox.Show(message, title, MessageBoxButtons.OK, MessageBoxIcon.Stop);
                Application.Idle += new EventHandler(Application_Idle);
                return;
            }
            catch (Exception ext)           // Все другие исключения
            {
                closing = true;
                MessageBox.Show(ext.Message);
                Application.Idle += Application_Idle1;
            }
        }
    
        void Application_Idle(object sender, EventArgs e)
        {
            this.Close();   // Закрывает окно, у нас оно же и главное окно приложения
        }
    
        void Application_Idle1(object sender, EventArgs e)
        {
            Application.Exit(); // Завершает работу приложения (приведено для разнообразия)
        }
  • Запустите приложение и попробуйте изменить разрешение экрана, чтобы поймать исключение невосстановимой потери устройства

В этом случае будет выдано окно с сообщением


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

В упражнении 1 наше приложение осуществляло монопольный вывод графической информации на всю поверхность формы. Такой подход не позволяет размещать на форме элементы управления. Можно попробовать использовать вспомогательные диалоговые окна или плавающие панели инструментов, но это не очень красивое решение проблемы. Гораздо интереснее было бы научиться выводить информацию не на саму форму, а на элементы управления, размещенные на ней (к примеру, на элемент управления Panel ). Это позволило бы на освободившемся пространстве размещать другие нужные элементы пользовательского интерфейса.

Все элементы управления наследуют от класса Control, в том числе класс Form и Panel. Ранее при управлении экземпляром класса Form мы фактически использовали методы и свойства класса Control. В данном упражнении мы с таким же успехом можем управлять экземпляром класса Panel, помещенным на форму.

Единственная сложность состоит в том, что при управлении экземпляром Form наш код размещался внутри его расширения - классе MainForm, и имел доступ ко всем общедоступным ( public ) и защищенным ( protected ) свойствам и методам, унаследованным, в конечном итоге, от класса Control. А если попытаться просто управлять экземпляром класса Panel, помещенным на форму, то видимыми из расширения класса Form в объекте Panel (и, соответственно, в Control ) будут только общедоступные члены. Чтобы получить полный доступ к нужным сервисам класса Control, нужно создать расширение класса Panel и упаковать в нем вызовы защищенных членов в общедоступные.

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

Создание заготовки пользовательского элемента управления

  • Закройте в рабочей области оболочки все редактируемые ранее документы командой меню Window/Close All Documents
  • Командой File/Add/New Project вызовите мастер добавления в решение нового проекта с именем XNAPanel и заполните его окно так

  • Через панель Solution Explorer переименуйте автоматически созданный файл Class1.cs на XNAPanel.cs
  • Командой Project/Add Reference подключите к проекту библиотечные сборки System.Windows.Forms.dll и System.Drawing.dll, а в начало файла XNAPanel.cs вставьте инструкции:
    using System.Windows.Forms;   
      using System.Drawing;
  • Модифицируйте XNAPanel.cs следующим образом
using System;
using System.Collections.Generic;
using System.Text;
    
using System.Windows.Forms;
using System.Drawing;
    
namespace XNAPanel
{
    public class XNAPanel : Panel
    {
        // Конструктор по умолчанию
        public XNAPanel()
        {
            // Изменим цвет фона панели, чтобы визуально отличить от других
            BackColor = Color.CornflowerBlue;
            // Установим минимальный размер, 
            // чтобы устранить возможное исключение
            MinimumSize = new Size(1, 1);
        }
    
        // Сделаем общедоступным нужный нам защищенный метод
        public new void SetStyle(ControlStyles flag, bool value)
        {
            base.SetStyle(flag, value);
        }
    }
}
  • Командой меню Build/Build XNAPanel откомпилируйте данный проект

Мы создали библиотечный пользовательский элемент управления для вывода графической информации XNA, который теперь можно размещать на форме.

  • Командой File/Add/New Project вызовите мастер добавления в решение нового проекта с именем Application2 и заполните его окно так

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

  • Через Обозреватель решений переименуйте файл Form1.cs проекта Application2 в MainForm.cs, поменяйте заголовок окна Text="Упражнение 2" и из панели инструментов Toolbox поместите на форму экземпляр компонента SplitContainer (можно просто выполнить двойной щелчок мышью на компоненте). Убедитесь, что свойство Dock объекта splitContainer1 имеет значение Fill, т.е. что объект занимает всю клиентскую область формы
  • Установите для объекта splitContainer1 свойство BorderStyle в значение FixedSingle, чтобы создать черные рамки вокруг панелей элемента
  • Выделите курсором левую панель Panel1 объекта splitContainer1, поместите в нее из панели Toolbox экземпляр нашего компонента XNAPanel и установите его свойство Dock в значение Fill, чтобы он занял всю область объекта Panel1
  • Выделите курсором правую панель объекта splitContainer1, поместите на нее экземпляр компонента GroupBox и разверните его на всю панель установкой свойства Dock в значение Fill. Присвойте объекту groupBox1 заголовок " Параметры " через его свойство Text
  • Выделите объект groupBox1 и добавьте в него три текстовых метки Label с надписями
    • Цвет вершины №1
    • Цвет вершины №2
    • Цвет вершины №3
  • Поместите напротив этих меток три экземпляра компонента Button и присвойте им имена vertex1Color, vertex2 Color и vertex3Color
  • Выделите все три экземпляра компонента Button, очистите для них содержимое свойства Text и установите свойство Size равным ( 30 ; 20 )
  • Настройте для каждой кнопки уникальный цвет фона через свойство BackColor
  • Добавьте к форме экземпляр невизуального компонента ColorDialog, пиктограмма которого будет размещена в подвале рабочей области ( Workspace ) дизайнера формы

В результате форма в режиме проектирования примет следующий вид


Средствами XNA мы нарисуем примитивный треугольник на экземпляре нашего компонента XNAPanel, цвет вершин которого мы будем выбирать из диалоговой панели. Для этого, как и ранее, нужно добавить в класс формы несколько вспомогательных полей, создать обработчики событий Load, FormClosed, Paint и т.д. и заполнить их соответствующим кодом. Но прежде, чем приступить к программированию обработчиков, познакомимся с новой техникой, которую мы будем использовать в этом упражнении для управления видеокартой на низком уровне.

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

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

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