FlatRedBall XNA in Windows Form

Implementing FRB in a form

This tutorial will explain how to implement FRB on a windows form (also known as WinForms). There are a few noticeable differences between the normal FRB template and using FRB in a form.

The requirements for this example are:

  • A form with 1 panel control

  • A class inheriting from Microsoft.Xna.Framework.Game

  • PeekMessage API call.

Known issues

There are currently a few known issues regarding using FRB in a windows form.

  • This tutorial is written for FlatRedBall XNA. It has not been tested and will probably not work with modern versions of FlatRedBall (MonoGame or FNA).

  • Mouse position also updates outside the form.

  • Method FlatRedBall.Input.Mouse.IsInGameWindow() can incorrectly return true whilst the cursor is outside the window.

Creating a new Project

The first step is to create a new Windows Forms Application project. To do this:

  • Open Visual Studio

  • Go to File->New Project...

  • Select "Windows Forms Application" under Visual Studio installed templates. You may need to select the "C#" category as "XNA Game Studio VERSION NUMBER" might be selected by default.

  • Enter a Name and press OK.

Adding FlatRedBall

FlatRedBall needs to be added to the project. To do this:

  • Right-click on your project

  • Select Add->New Folder

  • Rename the new folder "Libraries"

  • Locate the FlatRedBall.dll file. If you downloaded the template, then it'll be located inside the .zipped template file on your computer. On Windows Vista, it's probably:

C:\Users\<YOUR NAME>\Documents\Visual Studio 2008\Templates\ProjectTemplates

If you're not sure, you can always re-run the installer and see the folder location where it's saved.

  • You'll probably need to unzip the template to get access to the .dll. Remember, you'll still need the .zipped folder in the same location if you decide to make a FRB application at a later time. Remember to unzip the XNA template.

  • Once you've unipped the folder, navigate to its Libraries directory.

  • Drag the FlatRedBall.dll file from the unzipped Libraries folder into the Libraries folder in your project in the Solution Explorer.

  • For Intellisense support, drag the FlatRedBall.XML file into your project's Libraries folder. Right-click it and select "Exclude From Project".

  • Right-click on the References item under your project and select "Add Reference...".

  • Select the Browse tab.

  • Navigate to your Libraries folder.

  • Select FlatRedBall.dll and click OK.

Adding XNA

Just like FlatRedBall, your project needs to reference XNA. To add the necessary references:

  • Right-click on your project's References item and select "Add Reference...".

  • Click on the .NET tab.

  • Scroll down and select Microsoft.Xna.Framework.

  • Click "OK"

  • Repeat the above steps to add "Microsoft.Xna.Framework.Game" to your project as well.

PeekMessage API

To implement this feature we'll need a piece of code that is familiar to those that worked with the MDX version of FRB.

We'll implement this in a new class called NativeMethods. To do this:

  • Right-click on your project in the Solution Explorer

  • Select Add->Class...

  • Name your class NativeMethods

  • Add the following code:

Add the following using statement:

using System;
using System.Runtime.InteropServices;
using System.Security;

Copy the following code as your class:

   public static class NativeMethods
   {
       [StructLayout(LayoutKind.Sequential)]
       public struct Message
       {
           public IntPtr hWnd;
           public UInt32 msg;
           public IntPtr wParam;
           public IntPtr lParam;
           public UInt32 time;
           public System.Drawing.Point p;
       }

       [SuppressUnmanagedCodeSecurity] // We won't use this maliciously
       [DllImport("User32.dll", CharSet = CharSet.Auto)]
       public static extern bool PeekMessage(out Message msg, IntPtr hWnd,
        uint messageFilterMin, uint messageFilterMax, uint flags);

   }

Game code

Next we'll create our Game class. Basically this class can be copied from the template and modified; For this example however I'll discuss all the necessary bits and pieces that we need to add to get a working application.

To create a game class:

  • Right-click on your project

  • Select Add->Class...

  • Name your class RenderClass

  • Add the code to your class as follows:

Add the following using statement:

using System;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Content;
using FlatRedBall;

Create the class:

   public class RenderClass : Game
   {
       public GraphicsDeviceManager graphics;
       public ContentManager content;
       
   }

Here start the differences with the normal template code. We apply a small hack to force XNA to recreate the device context. Also note that the constructor ends with the Initialize() method due to the form being the messagepump parser.

Create the following method:

       public RenderClass() : base()
       {
           graphics = new GraphicsDeviceManager(this);
           content = new ContentManager(Services);

           IGraphicsDeviceManager graphicsDeviceManager =
            this.Services.GetService(typeof(IGraphicsDeviceManager)) as 
            IGraphicsDeviceManager;
 
           // Check if the graphicsDeviceManager has been initialised before continuing.
           if (graphicsDeviceManager != null) 
           {
               graphicsDeviceManager.CreateDevice();
               // base BeginDraw relies on this next line; see MS Feedback item 277959
               this.GetType().BaseType.GetField("graphicsDeviceManager", 
                System.Reflection.BindingFlags.Instance | 
                System.Reflection.BindingFlags.NonPublic).SetValue(this, graphicsDeviceManager);
           }

           Initialize();
       }

Next on the list is the Initialize() method. Here as well we are applying a small hack to let XNA get into the right execution context.

Create the following method:

       protected override void Initialize()
       {
           base.Initialize();
           this.BeginRun();
        
           FlatRedBall.FlatRedBallServices.InitializeFlatRedBall(this, this.graphics);
           FlatRedBall.FlatRedBallServices.IsWindowsCursorVisible = true;

           // Fetch the GameTime class. We will require this for the first update call.
           GameTime gt = this.GetType().BaseType.GetField("gameTime",
            System.Reflection.BindingFlags.Instance | 
            System.Reflection.BindingFlags.NonPublic).GetValue(this) as GameTime;

           this.Update(gt);

           this.GetType().BaseType.GetField("doneFirstUpdate", 
            System.Reflection.BindingFlags.Instance | 
            System.Reflection.BindingFlags.NonPublic).SetValue(this, true);
           
           // From this point you can add your own initialisation code.
           Sprite sprite = FlatRedBall.SpriteManager.AddSprite("redball.bmp");
           FlatRedBall.SpriteManager.AddSprite(sprite);

       }

Next are the usual methods that you'll also find in the FRB template with one exception. The dispose() override.

Create the following methods:

       protected override void Update(GameTime gameTime)
       {
           FlatRedBall.FlatRedBallServices.Update(gameTime);
           base.Update(gameTime);
       }

       protected override void Draw(GameTime gameTime)
       {
           FlatRedBall.FlatRedBallServices.Draw();
           base.Draw(gameTime);
       }

       protected override void Dispose(bool disposing)
       {
           this.EndRun();
           base.Dispose(disposing);
       }

Once those are pasted into your class, you can declare the game class in the form code.

What about a graphic?

Most samples on the FlatRedBall website use the redball.bmp image. If you made a new project then it likely doesn't have this file. So, be sure to add the redball.bmp graphic to the project as follows:

  • Navigate to this location on your computer.

  • Drag the redball.bmp image into your Project.

  • Select the redball.bmp item and press F4 to bring up the properties window.

  • Select the "Build Action" as "None" and the "Copy to Output Directory" to "Copy if newer".

Form code

In this application, your form is the primary window. Usually in XNA applications the Game class window is the primary window. We need to make a few modifications to the form to ensure proper execution. To do this:

  • Right-click on your Fo