Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Hosting XNA in a Window

0.00/5 (No votes)
28 Jul 2007 1  
My journey to unravel and discover a solution for hosting XNA in a WinForm UserControl

Introduction

After writing my first article on XNA, my next interest in XNA moved me to figuring out how to host an XNA Window inside a WinForm, so that I can provide additional controls and other UI elements as part of this concept I'm trying to develop. This has proven to be a considerably arduous task.

And while I've made a blog post about my research, I felt that it would be of benefit to the Code Project community to actually have a brief article on the topic, even though I did not come up with the actual solution. Instead, this article:

  • briefly describes the problem
  • takes a quick look at two other solutions I tried
  • presents a list of useful references that I discovered along the way

In the end, I found this website, which provides the C# source code for hosting an XNA Window as a user control. I'm not providing the download here as I didn't write it, so please visit Nuclex's website for the download.

So, the point of making this a Code Project article is to condense into a single article all the blogs and websites I visited to undestand the problem and find the solution. I certainly didn't come across the solution googling for "XNA WinForm". Hopefully, in a few days, other people will find this Code Project article using those keywords!

The First Problem: The Game Class Constructor

The primary problem is that the Game class initializes its own Form rather than letting you specify your Form/Control. This is a silly oversight by the designers. Let's use Reflector to understand the problem:

  1. The Game constructor has a call to EnsureHost
  2. EnsureHost is implemented as:

    if (this.host == null)
    {
      this.host = new WindowsGameHost(this);
    

  3. WindowsGameHost instantiates a WindowsGameWindow:

    public WindowsGameHost(Game game)
    {
      this.game = game;
      this.LockThreadToProcessor();
      this.gameWindow = new WindowsGameWindow();
    
  4. And WindowsGameWindow finally instantiates the WindowsGameForm, which is derived from Form:

    public WindowsGameWindow()
    {
      this.mainForm = new WindowsGameForm();

That's problem number one.

The Second Problem: The GraphicsDeviceManager CreateDevice Method

Problem number two is the GraphicsDeviceManager. In your game's derived Game class, you'll have in the constructor:

graphics = new GraphicsDeviceManager(this);

The GraphicsDeviceManager class adds two services:

game.Services.AddService(typeof(IGraphicsDeviceManager), this);
game.Services.AddService(typeof(IGraphicsDeviceService), this);

and then, in the CreateDevice method, the Form control created by the Game constructor is specified as the control host:

GraphicsDevice device = new GraphicsDevice(
  newInfo.Adapter, 
  newInfo.DeviceType, 
  this.game.Window.Handle, // <=====

  newInfo.CreationOptions, 
  newInfo.PresentationParameters);

So the problem is twofold -- the Game class creates its own Form and then that Window is passed to the GraphicsDevice constructor.

Silly Solution #1: Adding Controls to the Game Window

The very first idea I came across cast the GameWindow to a Control and added controls to it. These actually rendered on the XNA Window and worked for things like buttons but did not work for edit controls, I suspect because there is some issue with message processing in the host form, though I never investigated it further.

A Better Solution, But Not Great

The second workaround I found was almost right, hooking the PreparingDeviceSettings and overriding the DeviceWindowHandle:

public Game1(Form form)
{
  graphics = new GraphicsDeviceManager(this);
  content = new ContentManager(Services);
  this.form = form;
  graphics.PreparingDeviceSettings += 
      new EventHandler<PreparingDeviceSettingsEventArgs>
                    (OnPreparingDeviceSettings);
}

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

This (and other solutions I came across) worked fine except that the XNA Window would still appear, requiring a kludge to hide it in the Draw window:

protected override void Draw(GameTime gameTime)
{
  if (firstTime)
  {
    System.Windows.Forms.Control xnaWindow = 
      System.Windows.Forms.Control.FromHandle((this.Window.Handle));
    xnaWindow.Visible = false;
    firstTime = false;
  }
  ...

As well as properly disposing of it:

static class Program
{
  static Game1 game;
  /// <summary>

  /// The main entry point for the application.

  /// </summary>

  static void Main(string[] args)
  {
    Form form = new Form();
    form.Disposed += new EventHandler(OnDisposed);
    form.ClientSize = new System.Drawing.Size(800, 600);
    form.Show();

    using (game = new Game1(form))
    {
      game.Run();
    }
  }

  static void OnDisposed(object sender, EventArgs e)
  {
    game.Exit();
  }
}

Other implementations, which are actually very straight forward (see references) typically eliminate the Game class. This wasn't really my goal, as I wanted to preserve the features of the Game class if at all possible. This led me to the conclusion that I would probably have to use Reflector to recreate specific classes of the framework. At this point, I shelved the whole effort and went back to work that actually pays the bills.

The Nuclex Solution

After putting in some billable hours, I revisited this and I came across a link in a reply to one of the references below that I hadn't checked out, and to my amazement, it was exactly what I was looking for. And indeed, the author does use reflector to recreate certain classes so as to gain control over the dual problems mentioned above. The link is here, and the implementation is very nice -- it's a user control, from which you derive your own class and work with the overridable methods just as you would from the Game class. I ported my test application over in minutes using the Nuclex.GameControl class.

I must say though that I'm still not happy with having to rewrite major sections of the framework. Hopefully the XNA framework architects will fix this problem in the future, and second, I still have a nagging feeling that there must be an easier way.

References

Various references (I found a lot of good information reading the comments as well):

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here