Display an XNA view directly on a WPF window

Page update date :
Page creation date :

summary

Describes how to render directly in a WPF window with XNA.

WPF ウインドウ上に直接 XNA のビューを表示する

Operating environment

Prerequisites

Supported XNA Versions
  • 2.0
  • 3.0
Supported Platforms
  • Windows (XP SP2 or later, Vista)
Windows Required Vertex Shader Version 1.1
Windows Required Pixel Shader Version 1.1

Operating environment

platform

substance

In Using XNA in WPF Applications, I used the WindowsFormsHost control to display the view, but now I want to use only the WPF window to display the polygon. "Using XNA in WPF Applications" is a bit of a representation, so I'll explain the differences.

Namespace aliases

// 名前空間エイリアス
using Xna = Microsoft.Xna.Framework;
using XnaGraphics = Microsoft.Xna.Framework.Graphics;

Because the Color and Matrix structures are covered by the XNA Framework and WPF namespaces, you must specify the name of the structure from the namespace in order to use it. However, it seems to be long every time, so I am creating a namespace alias. For example, "Microsoft.Xna.Framework.Rectangle" can now be specified as "Xna.Rectangle".

timer

Since the .NET Framework 3.0, a timer class called "System.Windows.Threading.DispatcherTimer" has been available, and this timer class is used to render every frame. WPF uses a DispatcherTimer to allow processing on the same thread as the UI.

/// <summary>
/// タイマー
/// </summary>
private DispatcherTimer timer = null;

field, declaring a DispatcherTimer.

/// <summary>
/// タイマーイベント
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void dispatcherTimer_Tick(object sender, EventArgs e)
{
    this.Draw();
}

Defines a method that the DispatcherTimer calls at regular intervals.

// タイマー
this.timer = new DispatcherTimer();
this.timer.Tick += new EventHandler(dispatcherTimer_Tick);
this.timer.Interval = new TimeSpan(0, 0, 0, 0, 16);
this.timer.Start();

This is the process of setting the timer. After creating an instance of DispatcherTimer and setting the method to call and the time interval, the DispatcherTimer.Start method starts the timer.

Obtaining a Window Handle

To create a Direct3D device, you need a window handle. However, WPF doesn't have the concept of a window handle, so there's no way to get it directly from the Window class.

However, since there are times when a window handle is required, as in this case, the window handle can be obtained via the "System.Windows.Interop.WindowInteropHelper" class as an interop for Win32 code. (this is the Window class)

// ウィンドウハンドルを取得する
IntPtr handle = new WindowInteropHelper(this).Handle;

Use this handle when creating a Direct3D device. However, if you try to get the window handle in the OnInitialized method, it will return 0, probably because the window has not been created yet. In this sample, the device is created in the OnSourceInitialized method.

Render to Specified Area

You can specify the destination area as the second argument of GraphicsDevice.Present. In the sample, I try to draw it at the position (50, 50) as an offset. The size is the same as the size of the backbuffer (300, 300). The first argument is the area from which to draw, and if null is specified, the original buffer is used.

// 描画先を指定する
Xna.Rectangle rect = new Xna.Rectangle(
    50, 50,
    (int)this.viewSize.Width, (int)this.viewSize.Height);

this.device.Present(null, rect, this.windowHandle);

Destroying a device

You're destroying your device when you close the window. Originally, I wanted to handle it with the Dispose method, but since the WPF Window class does not inherit IDisposable by default, I wrote it here instead.

/// <summary>
/// ウインドウが閉じたとき
/// </summary>
/// <param name="e"></param>
protected override void OnClosed(EventArgs e)
{
    if (this.device != null)
    {
        this.device.Dispose();
        this.device = null;
    }

    base.OnClosed(e);
}

execution

If you run the sample program, you can run it with a screen image like the one at the beginning of the article.

At first glance, it renders well, but when I resize the window by dragging it with the mouse, the view flickers or disappears during resizing. It seems to be conflicting with WPF rendering, and I tried a lot of things but couldn't solve it.

If you're using XNA, it seems safer to use the WindowsFormsHost control.

The program requires the .NET Framework 3.0, the latest DirectX runtime, and the Microsoft XNA Framework Redistributable 2.0. In addition, as a hardware requirement, "a graphics card that supports Pixel Shader 1.1 or higher" is required.

In addition, the project was created in "Visual Studio 2008 Professional Edition". Prepare an environment for Visual Studio 2008.