Click here to Skip to main content
13,866,116 members
Click here to Skip to main content
Add your own
alternative version


67 bookmarked
Posted 20 Jul 2009
Licenced CPOL

XNA integration inside WPF

, 20 Jul 2009
Rate this:
Please Sign up or sign in to vote.
Another way to integrate multiple XNA scenes inside WPF.


There are many ways to display 3D scenes based on XNA in a WPF environment. Some suffer from problems of speed (with the use of WindowsFormHost), others allow only limited interaction with the WPF controls and interfaces. Implementing multiple displays as seen in programs like Maya becomes problematic:

There is a relatively simple way to do this. This way is simply to give a visual impression to the user of a perfect integration of XNA in a WPF widget when it is not. The key to this lies in the perfect handling of windows.


We want to integrate our XNA scene in a WPF user interface in the same way that we integrate a canvas or any widget. But the best way to view a 3D scene in XNA is to incorporate it in a window. It is impossible to obtain the handle of any WPF control as can be done with a WinForm. The trick then is to rewrite part of the XNA framework revolving around the Game class. The goal is to inherit a new Game class from Panel (in our case, a Canvas) to be able to include it in the WPF visual tree. The visual bounds of the panel will be the viewing area of the XNA scene. Yet, we have just said that it is not possible to obtain a handle to a visual control that does not inherit from Window. How do we display 3D with XNA then? We will simply display a window without border just above the panel. This window will always be on top when the application has focus and the panel is visible and will be hidden in this case. Similarly, when the panel is not visible, we will halt the activity of the game.

Each change of size or position of the panel will cause a change equivalent to the window above it.


This window is located exactly above the panel and has the same size: the illusion is perfect.


The first step is to rewrite part of the classes in the assemblies Microsoft.Xna.Framework and Microsoft.Xna.Framework.Game. The project Arcane.Xna.Presentation has some classes of these assemblies for use with WPF.


Nothing very complicated. Only the Game and GameHost classes are really interesting here.

The Game class, as stated above, is the display of 3D scenes in WPF user interfaces. It inherits from Canvas. Using Canvas meets a particular need that we present below. The Game class is distinguished from the Game class in the Microsoft.Xna.Framework.Game assembly by only a few members. First, it has a member type named GameHost which is the window that is positioned just above. It also has a member _tichGenerator which will update the display at regular intervals.

The constructor initializes its members as follows:

this._window = new GameHost(this);
this._window.Closed += new EventHandler(_window_Closed);
this._tickGenerator = new DispatcherTimer();
this._tickGenerator.Tick += new EventHandler(_tickGenerator_Tick);

It starts by creating the window to be located above it, and registers the Closed event to close the 3D scene. The object DispatcherTimer is used to recreate a loop game by regularly calling the Update and Draw methods. Its velocity depends on the property IsFixedTimeStep.

The last important element, the recording of the event IsVisibleChange:

this.IsVisibleChanged += new 

The activation of DispatcherTimer is based on visibility.

The GameHost class is simple too. It creates a borderless window not visible in the taskbar, and records the SizeChanged event of the panel and the XNA LocationChanged event from the window at the highest level. Both events allow it to always be above the panel XNA by making a call to the method UpdateBounds:

public void UpdateBounds()
    if (this.IsVisible)
        GeneralTransform gt =;
        this.Width =;
        this.Height =;
        this.Left = this.TopLevelWindow.Left + gt.Transform(new Point(0, 0)).X;
        this.Top = this.TopLevelWindow.Top + gt.Transform(new Point(0, 0)).Y;

This method determines the position of the current window by using the top level window (the window containing the panel of the XNA Game class). It also ensures the same width and height as the panel XNA.

Once again, the window registers on the event IsVisibleChange if it to be visible or not.

First example

We will base our example on the framework AvalonDock (, an effective way to create interfaces to be dockable on Visual Studio easily. Also, a way for us to show the power and simplicity of our system in WPF interfaces.

Our solution contains a project called Demo which corresponds to an example of AvanlonDock, nut slightly modified. We added a class inheriting from Game that will display a rotating cube on itself.

This class was simply extracted from a pure XNA application to be added in this project, without any change (or almost):

public class RotatingCubeGame : Arcane.Xna.Presentation.Game
    #region Fields
    Arcane.Xna.Presentation.GraphicsDeviceManager graphics;
    SpriteBatch spriteBatch;
    BasicEffect effect;
    VertexPositionColor[] vertices;
    Vector3 position = Vector3.Zero;
    Vector3 size = Vector3.One;
    VertexBuffer vertexBuffer;
    IndexBuffer indexBuffer;
    #region Constructors
    public RotatingCubeGame()
        if (!(System.ComponentModel.DesignerProperties.GetIsInDesignMode(this)))
            graphics = new Arcane.Xna.Presentation.GraphicsDeviceManager(this);
            Content.RootDirectory = "Content";
    /// <summary>
    /// Allows the game to perform any initialization it needs to before starting to run.
    /// This is where it can query for any required services and load any non-graphic
    /// related content.  Calling base.Initialize will enumerate through any components
    /// and initialize them as well.
    /// </summary>
    protected override void Initialize()
       // TODO: Add your initialization logic here = false; = 800; = 600;;
        this.Window.Title = "";
    private void InitializeVertices()
        vertices = new VertexPositionColor[8];
        vertices[0].Position = new Vector3(-10f, -10f, 10f);
        vertices[0].Color = Color.Yellow;
        vertices[1].Position = new Vector3(-10f, 10f, 10f);
        vertices[1].Color = Color.Green;
        vertices[2].Position = new Vector3(10f, 10f, 10f);
        vertices[2].Color = Color.Blue;
        vertices[3].Position = new Vector3(10f, -10f, 10f);
        vertices[3].Color = Color.Black;
        vertices[4].Position = new Vector3(10f, 10f, -10f);
        vertices[4].Color = Color.Red;
        vertices[5].Position = new Vector3(10f, -10f, -10f);
        vertices[5].Color = Color.Violet;
        vertices[6].Position = new Vector3(-10f, -10f, -10f);
        vertices[6].Color = Color.Orange;
        vertices[7].Position = new Vector3(-10f, 10f, -10f);
        vertices[7].Color = Color.Gray;
        this.vertexBuffer = new VertexBuffer(, 
             typeof(VertexPositionColor), 8, BufferUsage.WriteOnly);
    private void InitializeIndices()
        short[] indices = new short[36]{    
            0,1,2, //face devant
            3,2,4, //face droite                
            5,4,7, //face arrière                
            6,7,1, //face gauche
            6,0,3, //face bas                
            1,7,4, //face haut                
        this.indexBuffer = new IndexBuffer(, 
                           typeof(short), 36, BufferUsage.WriteOnly);
    /// <summary>
    /// LoadContent will be called once per game and is the place to load
    /// all of your content.
    /// </summary>
    protected override void LoadContent()
        // Create a new SpriteBatch, which can be used to draw textures.
        spriteBatch = new SpriteBatch(GraphicsDevice);
        // TODO: use this.Content to load your game content here
        this.effect = new BasicEffect(graphics.GraphicsDevice, null);
        this.effect.View = (Matrix.CreateLookAt(new Vector3(20, 30, -50), 
                            Vector3.Zero, Vector3.Up));
        this.effect.Projection = (Matrix.CreatePerspectiveFieldOfView(
          MathHelper.PiOver4, this.GraphicsDevice.Viewport.AspectRatio, 0.1f, 100f));
       // this.effect.EnableDefaultLighting();
       // this.effect.LightingEnabled = true;
        this.effect.VertexColorEnabled = true;
    /// <summary>
    /// Allows the game to run logic such as updating the world,
    /// checking for collisions, gathering input and playing audio.
    /// </summary>
    /// <param name="gameTime">Provides a snapshot
    ///     of timing values.</param>
    protected override void Update(GameTime gameTime)
        if (Keyboard.GetState()[Keys.Up] == KeyState.Down)
            position += Vector3.Up;
        if (Keyboard.GetState()[Keys.Down] == KeyState.Down)
            position += Vector3.Down;
        if (Keyboard.GetState()[Keys.Left] == KeyState.Down)
            position += Vector3.Left;
        if (Keyboard.GetState()[Keys.Right] == KeyState.Down)
            position += Vector3.Right;
        if (Keyboard.GetState()[Keys.PageUp] == KeyState.Down)
            size += new Vector3(0.1f, 0.1f, 0.1f);
        if (Keyboard.GetState()[Keys.PageDown] == KeyState.Down)
            size -= new Vector3(0.1f, 0.1f, 0.1f);
        // Allows the default game to exit on Xbox 360 and Windows
        if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)
        float fAngle = (float)gameTime.TotalGameTime.TotalSeconds;
        //la transformation en elle même
        Matrix world = Matrix.CreateRotationY(fAngle) * Matrix.CreateRotationX(fAngle)
                            * Matrix.CreateScale(size)
                            * Matrix.CreateTranslation(position);
        this.effect.World = (world);
    /// <summary>
    /// This is called when the game should draw itself.
    /// </summary>
    /// <param name="gameTime">Provides a snapshot of timing values.</param>
    protected override void Draw(GameTime gameTime)
    {[0].SetSource(this.vertexBuffer, 0, 
                                     VertexPositionColor.SizeInBytes); = this.indexBuffer; = 
          new VertexDeclaration(, 
        // TODO: Add your drawing code here    
        foreach (EffectPass pass in effect.CurrentTechnique.Passes)
                           PrimitiveType.TriangleList, 0, 0, 8, 0, 12);

Nothing very complicated here for anyone who knows XNA. We just show here a rotating cube. The first change is to inherit the class RotatingCubeGame from the Game class of our assembly and not the Game class of the Microsoft.Xna.Framework.Game assembly. The second modification is to surround the initializations made in the constructor:


to ensure that our game will not be created as part of the Visual Studio designer. The rest is very simple. We just replace the contents of each DockablePane in the XAML code in Window1 by:


The result gives us:

Obviously, our system complies with the advantage of allowing AvalonDock docking strong and without disrupting our 3D scene:


Not so bad, but we can do better.

Widgets integration

Why not try to display widgets (Button, Label, Grid, Canvas, ...) in our 3D scene for a perfect integration with WPF?

We might be tempted to add these items directly to the window GameHost. But we would have flicker problem (two different types of displays -3D and vector- on the same clip area is not necessarily good ...). We will simply add a new window above the existing window:


Its content will be directly connected to the content of the panel XNA (the Canvas). The class GameHost will have a new member named _frontWindow (which is a Window). It sets out in the internal property named WPFHost giving access to the content of this window:

internal object WPFHost
        return this._frontWindow.Content;
        this._frontWindow.Content = value;

The Game class will also expose the content of this window with a property using the same name:

public object WPFHost
        if (!(System.ComponentModel.DesignerProperties.GetIsInDesignMode(this)))
            return this.Window.WPFHost;
            return (base.Children[0] as ContentControl).Content;
        if (!(System.ComponentModel.DesignerProperties.GetIsInDesignMode(this)))
            this.Window.WPFHost = value;
            (base.Children[0] as ContentControl).Content = value;

This property determines if we are in Design mode (in Visual Studio) or in runtime mode. In Design mode, we use the Canvas class which inherits Game; in runtime mode, we directly target the window. This allows us, in the Visual Studio Designer, to be able to see and modify the UI of our control with the mouse.

In addition, we mark the class attribute of Game:

[System.Windows.Markup. ContentProperty ( "WPFHost" )] 
[System.Windows.Markup. ContentProperty ( "WPFHost")] 

We enable direct content in XAML:


Window1.xaml is modified to add more content to the RotatingCubeGame canvas as shown in the image above. We added pure shapes and paths to reproduce the character orange and yellow, symbolizing XNA, buttons, and labels associated with events, and FlowDocument with scrolling.


The assembly Arcane.Xna.Presentation presents a simple way to integrate professional XNA applications to WPF. The only real flaws that can be found is the code, realized very quickly due to time and the creation of two windows by XNA game classes. The number of displayable windows under Windows is unfortunately limited. The result still works perfectly, and can be used for professional applications:


Screenshot of my editor world

You can download the latest source code here: valentin.articles.CoursXna.annexe3/


This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


About the Author

Valentin Billotte
Architect Viablue - GraphicStream
France France
I spent most of time on Silverlight, Xna (where i am MVP) and ADO.Net Data Services.

You may also be interested in...

Comments and Discussions

QuestionTexture problem Pin
Member 1071054011-Apr-14 11:50
memberMember 1071054011-Apr-14 11:50 
QuestionDrawableGameComponent equalnt Arcane.Xna.Presentation Pin
imalperera18-Apr-12 21:06
memberimalperera18-Apr-12 21:06 
QuestionThis project has been ported to XNA 4 Pin
Member 81030683-Feb-12 16:49
memberMember 81030683-Feb-12 16:49 
AnswerRe: This project has been ported to XNA 4 Pin
rajbow19-Mar-12 2:22
memberrajbow19-Mar-12 2:22 
AnswerRe: This project has been ported to XNA 4 Pin
Member 777794618-Sep-12 22:27
memberMember 777794618-Sep-12 22:27 
GeneralRe: This project has been ported to XNA 4 Pin
Member 290973710-Oct-12 20:15
memberMember 290973710-Oct-12 20:15 
QuestionXNA 4.0 support? Pin
JeeReeT3-Oct-11 6:15
memberJeeReeT3-Oct-11 6:15 
QuestionAny possibility of porting to XNA 4.0? Pin
ely_bob10-Apr-11 10:17
professionalely_bob10-Apr-11 10:17 
AnswerRe: Any possibility of porting to XNA 4.0? Pin
LuisEstrada12-Sep-11 15:02
memberLuisEstrada12-Sep-11 15:02 
GeneralRe: Any possibility of porting to XNA 4.0? Pin
ely_bob12-Sep-11 15:06
professionalely_bob12-Sep-11 15:06 
QuestionWhy didn't you inherit from the XNA Game class? Pin
VG-Alchemist21-Aug-10 1:39
memberVG-Alchemist21-Aug-10 1:39 
AnswerRe: Why didn't you inherit from the XNA Game class? Pin
Valentin Billotte3-Nov-10 4:03
memberValentin Billotte3-Nov-10 4:03 
GeneralGreat post. Thanks! Pin
chbfiv1-Aug-10 5:55
memberchbfiv1-Aug-10 5:55 
GeneralOutOfVideoMemoryException again Pin
OCNVN18-Apr-10 11:31
memberOCNVN18-Apr-10 11:31 
GeneralRe: OutOfVideoMemoryException again Pin
Valentin Billotte27-Apr-10 23:04
memberValentin Billotte27-Apr-10 23:04 
GeneralRe: OutOfVideoMemoryException again Pin
chbfiv1-Aug-10 5:54
memberchbfiv1-Aug-10 5:54 
QuestionA few pixels off [modified] Pin
Matthijs Wessels28-Mar-10 7:00
memberMatthijs Wessels28-Mar-10 7:00 
AnswerRe: A few pixels off Pin
Valentin Billotte28-Mar-10 22:40
memberValentin Billotte28-Mar-10 22:40 
GeneralRe: A few pixels off Pin
Matthijs Wessels28-Mar-10 23:26
memberMatthijs Wessels28-Mar-10 23:26 
GeneralRe: A few pixels off Pin
Valentin Billotte29-Mar-10 10:31
memberValentin Billotte29-Mar-10 10:31 
QuestionDrawableGameComponent Pin
unknown4774-Feb-10 6:42
memberunknown4774-Feb-10 6:42 
AnswerRe: DrawableGameComponent Pin
Valentin Billotte18-Feb-10 21:17
memberValentin Billotte18-Feb-10 21:17 
GeneralOutOfVideoMemoryException Pin
Zeitroid30-Nov-09 16:37
memberZeitroid30-Nov-09 16:37 
GeneralRe: OutOfVideoMemoryException Pin
Valentin Billotte30-Nov-09 20:58
memberValentin Billotte30-Nov-09 20:58 
GeneralRe: OutOfVideoMemoryException Pin
Zeitroid1-Dec-09 4:17
memberZeitroid1-Dec-09 4:17 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

Permalink | Advertise | Privacy | Cookies | Terms of Use | Mobile
Web05 | 2.8.190214.1 | Last Updated 20 Jul 2009
Article Copyright 2009 by Valentin Billotte
Everything else Copyright © CodeProject, 1999-2019
Layout: fixed | fluid