XNA Framework GameEngine Development. (Part 15, Adding WinForm Support, step 1 to World Builder)
Welcome to Part15 of the XNA Framework GameEngine Development series. This article will introduce using XNA 2.0 in the WinForms environment. This is the first obvious step to creating a World Builder application. I know the XNA team site has an example of how to do this, but I simply did not like how truely difficult it was to get an XNA Game into WinForms using their example.
So I turned again to the open source community and was not disappointed. Pedro Güida was kind enough to show an example of doing exactly what I want to do on the codeproject resource site. This article is my implementation of his work, with a few minor tweeks.
Release
Here is the sourcecode, if you like this series or have suggestions please leave a comment. Your feedback is very important and is fuel for new ideas.
Simple steps to WinForms
-
Create a Game Project (DONE) – this is what we have been doing all along.
-
Create a Windows Form in our Engine project.
-
Avoid using any references to Windows Forms on the XBOX360.
-
Hide the game window – use the window form handle as the presentation device handle.
Adding a WinForm
Add a new winform to the RoeEngine2 project. Add a panel to the project window and add a few properties for use later.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
namespace RoeEngine2
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
public Panel XNAPanel
{
get { return panel1; }
}
public int PanelWidth
{
get { return panel1.Width; }
}
public int PanelHeight
{
get { return panel1.Height; }
}
public IntPtr PanelHandle
{
get
{
return this.panel1.IsHandleCreated ? this.panel1.Handle : IntPtr.Zero;
}
}
}
}
Updates to base engine
In the RoeEngine class we need to do some work. Our main goal here is to hide the game form, handle window resize, window close, and Show the WinForm. Then copy everything we would have drawn in the game window to the new WinForm window.
In the Initialize method add the following code.
#if !XBOX360
if (_useWinForm)
{
SysWinForms.Form gameWindowForm = (SysWinForms.Form)SysWinForms.Form.FromHandle(this.Window.Handle);
gameWindowForm.Shown += new EventHandler(gameWindowForm_Shown);
myForm = new Form1();
myForm.HandleDestroyed += new EventHandler(myForm_HandleDestroyed);
myForm.Resize += new EventHandler(myForm_Resize);
myForm.XNAPanel.Resize += new EventHandler(XNAPanel_Resize);
myForm.Show();
}
#endif
Wire up some event handlers for resizing and destroying windows.
#if !XBOX360
void XNAPanel_Resize(object sender, EventArgs e)
{
ApplyResolutionChange(((SysWinForms.Panel)sender).Width, ((SysWinForms.Panel)sender).Height);
}
void myForm_Resize(object sender, EventArgs e)
{
ApplyResolutionChange(((Form1)sender).PanelWidth, ((Form1)sender).PanelHeight);
}
void myForm_HandleDestroyed(object sender, EventArgs e)
{
this.Exit();
}
void gameWindowForm_Shown(object sender, EventArgs e)
{
((SysWinForms.Form)sender).Hide();
}
#endif
Finally, show something to the user. Add the following code to the Draw method.
#if !XBOX360
if (_useWinForm)
{
GraphicsDevice.Present(myForm.PanelHandle);
}
#endif
Yes, it is that easy, I do not quite understand why the XNA team site went through so much trouble in their sample. Could be my inexperience, I would like to discuss possible changes to this solution if some smart folks have anything to offer. For now, this seems simple enough for what we need to do.
World Builder project
For completeness, I like to have a seperate project that will be my world builder startup project. This is pretty much the exact same thing as the “normal” game project. Just this time, we pass true as the useWinForms parameter when creating the game.
using (EngineManager game = new EngineManager(true))
{
EngineManager.Game = game;
SetupScene();
game.Run();
}
Conclusion
In this article I introduced using XNA in a WinForm project. This will be the beginning of our World Builder project and gives us a reasonably useful way to easily manipulate the engine.
I look forward to your suggestions and feedback. I know the XNA team site does this in a very different way from what I am doing. I would very much like to hear your thoughts and opinions about this solution.