Pull to refresh

Дружим XNA и WPF

Reading time 3 min
Views 9.1K
Вначале отвечу на вопрос «Зачем их дружить». Ответ прост — WPF хорош для пользовательских интерфейсов, XNA для сложной 3D графики и если вы делаете клиентское приложение со сложным интерфейсом и 3D элементами в нем, то связка XNA & WPF как раз для вас.
Статью я буду иллюстрировать на примере простенького медиаплеера, который я сейчас пишу.

Итак, у нас есть скроллер обложек написанный на XNA. Выглядит он так:



А еще у нас есть WPF проект будущего медиаплеера, вот его скриншот:



Наша задача состоит в том, чтобы поместить первое во второе. Поступим также, как поступали ранее с приложениями на Windows Forms: передадим в конструктор класса Game хендл на элемент управления, поверх которого мы будем рисовать. Но есть одна небольшая проблема: в WPF хендлами обладают только окна. Впрочем, эта проблема легко решаема: добавим в окно элемент управления WindowsFormsHost и поместим в него панель (Panel) из пространства имен System.Windows.Forms. И вот хендл этой панели мы и передадим в конструктор игры. Итак, действуем по порядку:
  1. Объединим XNA и WPF проекты в одном решении
  2. Удаляем у XNA проекта файл Program.cs и в свойствах проекта меняем тип с Windows приложения на библиотеку классов.
  3. Добавим Microsoft.Xna.Framework.Game в References WPF проекта
  4. Добавим в объявление конструктора игры параметр IntPtr handle
  5. В конструкторе формы WPF создадим экземпляр класса игры и передаем ему хендл на панель и вызовем у игры метод Run() в отдельном потоке
  6. Во время инициализации устройства присвоим наш хендл

Поясню последний пункт:
IntPtr Handle;
public MainGame(IntPtr handle)
{
  Handle = handle;
  graphics = new GraphicsDeviceManager(this);
  graphics.PreparingDeviceSettings += new EventHandler<PreparingDeviceSettingsEventArgs>(PreparingDeviceSettings);
}

public void PreparingDeviceSettings(object sender, PreparingDeviceSettingsEventArgs e)
{
  e.GraphicsDeviceInformation.PresentationParameters.DeviceWindowHandle = Handle;
}


* This source code was highlighted with Source Code Highlighter.


Отлично, наша «игра» отрисовывается в отведенную для нее панель, но есть несколько пунктов, по которым меня не устраивает данное решение:
  • При запуске приложения параллельно создается какое-то левое окно. Его конечно можно скрыть, но все равно это нехорошо.
  • При изменении размеров формы у нас происходит тупое растягивание/сжатие картинки. Хотелось бы реинициализировать устройство для обеспечения наилучшего качества картинки.
  • На мой взгляд, самое главное: игра и приложение живут в разных потоках. Это приводит к ошибкам при попытке работы с WPF-контролами из XNA кода.
  • Панель предательски моргает при изменении ее размеров.

Будем решать эти проблемы. Для этого я предлагаю прекратить использование класса игры и написать небольшой менеджер устройства. Нам придется вручную инициализировать устройство, по таймеру перерисовывать сцену, откликаться на изменение размеров контрола и его уничтожение. В менеджер мы передаем контрол, на котором будем рисовать, подписываемся на события ресайза и отрисовки контрола, создаем устройство и таймер, по которому мы будем вызывать инициировать события Update и Draw. Вы можете посмотреть исходный код графического менеджера здесь или в прилагаемом проекте. Нам осталось лишь разобраться с панелью. Оказывается нужно лишь применить соответствующие стили и панель перестанет моргать. Неудобство лишь в том, что модификатор доступа метода SetStyle — protected. Ну ничего, унаследуемся от панели и в конструкторе применим нужные нам стили. Исходный код оптимизированной панели прост до безобразия:

public class OptimizedPanel : Panel
{
  public OptimizedPanel()
   : base()
  {
   this.SetStyle(ControlStyles.UserPaint, true);
   this.SetStyle(ControlStyles.Opaque, true);
  }
}

* This source code was highlighted with Source Code Highlighter.


Итак, у нас все готово, запускаем проект и видим, что все работает как надо: панелька не моргает, корректно меняются размеры, приложение не открывает дополнительных окон.
Здесь вы можете скачать начальный проект приложения со связкой WPF & XNA. Ну а вот что получилось у меня:

Tags:
Hubs:
+26
Comments 21
Comments Comments 21

Articles