1
\$\begingroup\$

I was following the book "XNA 4.0 Game Development By Example" and in the eighth chapter, the author adds windows form to an XNA game. I was following the book for a MonoGame project, but I get null reference exception on the line "gameForm.visibleChanged += new EventHandler(gameForm_visibleChanged)" and I cannot comprehend what's going on. When trying to use Console.WriteLine(gameForm), nothing is returned. this.Window.Handle returns a value, System.Windows.Forms.Control.FromHandle(this.Window.Handle) does not.

Here is the Game1.cs file :

using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
using TileEngine;

namespace LevelEditor
{
    /// <summary>
   /// This is the main type for your game
   /// </summary>
  public class Game1 : Game
{
    GraphicsDeviceManager graphics;
    SpriteBatch spriteBatch;
    IntPtr drawSurface;
    System.Windows.Forms.Form parentForm;
    System.Windows.Forms.PictureBox pictureBox;
    System.Windows.Forms.Control gameForm;

    public int DrawLayer = 0;
    public int DrawTile = 0;
    public bool EditingCode = false;
    public string CurrentCodeValue = "";
    public string HoverCodeValue = "";

    public MouseState lastMouseState;
    System.Windows.Forms.VScrollBar vscroll;
    System.Windows.Forms.HScrollBar hscroll;

    public Game1(IntPtr drawSurface,
                System.Windows.Forms.Form parentForm,
                System.Windows.Forms.PictureBox surfacePictureBox)
    {
        graphics = new GraphicsDeviceManager(this);
        Content.RootDirectory = "Content";

        this.drawSurface = drawSurface;
        this.parentForm = parentForm;
        this.pictureBox = surfacePictureBox;

        graphics.PreparingDeviceSettings +=
            new EventHandler<PreparingDeviceSettingsEventArgs>(
            graphics_PreparingDeviceSettings);

        Mouse.WindowHandle = drawSurface;

        gameForm = System.Windows.Forms.Control.FromHandle(this.Window.Handle);

        gameForm.VisibleChanged += new EventHandler(gameForm_VisibleChanged);

        pictureBox.SizeChanged += new EventHandler(pictureBox_SizeChanged);

        vscroll =
            (System.Windows.Forms.VScrollBar)parentForm.Controls["vScrollBar1"];
        hscroll =
            (System.Windows.Forms.HScrollBar)parentForm.Controls["hScrollBar1"];

        IsMouseVisible = true;
    }

    void graphics_PreparingDeviceSettings(object sender,
        PreparingDeviceSettingsEventArgs e)
    {

        e.GraphicsDeviceInformation.PresentationParameters.DeviceWindowHandle = drawSurface;

    }

    private void gameForm_VisibleChanged(object sender, EventArgs e)
    {
        if (gameForm.Visible == true)
            gameForm.Visible = false;
    }

    void pictureBox_SizeChanged(object sender, EventArgs e)
    {
        if (parentForm.WindowState !=
            System.Windows.Forms.FormWindowState.Minimized)
        {
            graphics.PreferredBackBufferWidth = pictureBox.Width;
            graphics.PreferredBackBufferHeight = pictureBox.Height;
            Camera.ViewPortWidth = pictureBox.Width;
            Camera.ViewPortHeight = pictureBox.Height;
            graphics.ApplyChanges();
        }
    }

    /// <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

        base.Initialize();
    }

    /// <summary>
    /// LoadContent will be called once per game and is the place to load
    /// all of your content.
    /// </summary>
    protected override void LoadContent()
    {
        spriteBatch = new SpriteBatch(GraphicsDevice);

        Camera.ViewPortWidth = pictureBox.Width;
        Camera.ViewPortHeight = pictureBox.Height;
        Camera.WorldRectangle =
            new Rectangle(
                0,
                0,
                TileMap.TileWidth * TileMap.MapWidth,
                TileMap.TileHeight * TileMap.MapHeight
            );

        TileMap.Initialize(
            Content.Load<Texture2D>(@"Textures\MarioTiles"));

        TileMap.spriteFont =
            Content.Load<SpriteFont>(@"Fonts\Pericles8");

        lastMouseState = Mouse.GetState();

        pictureBox_SizeChanged(null, null);
    }


    /// <summary>
    /// UnloadContent will be called once per game and is the place to unload
    /// all content.
    /// </summary>
    protected override void UnloadContent()
    {
        // TODO: Unload any non ContentManager content here
    }

    /// <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)
    {
        Camera.Position = new Vector2(hscroll.Value, vscroll.Value);

        MouseState ms = Mouse.GetState();

        if ((ms.X > 0) && (ms.Y > 0) &&
            (ms.X < Camera.ViewPortWidth) &&
            (ms.Y < Camera.ViewPortHeight))
        {
            Vector2 mouseLoc = Camera.ScreenToWorld(
                new Vector2(ms.X, ms.Y));

            if (Camera.WorldRectangle.Contains(
                (int)mouseLoc.X, (int)mouseLoc.Y))
            {
                if (ms.LeftButton == ButtonState.Pressed)
                {
                    TileMap.SetTileAtCell(
                      TileMap.GetCellByPixelX((int)mouseLoc.X),
                      TileMap.GetCellByPixelY((int)mouseLoc.Y),
                      DrawLayer,
                      DrawTile);
                }

                if ((ms.RightButton == ButtonState.Pressed) &&
                    (lastMouseState.RightButton == ButtonState.Released))
                {
                    if (EditingCode)
                    {
                        TileMap.GetMapSquareAtCell(
                          TileMap.GetCellByPixelX((int)mouseLoc.X),
                          TileMap.GetCellByPixelY((int)mouseLoc.Y)
                        ).CodeValue = CurrentCodeValue;
                    }
                    else
                    {
                        TileMap.GetMapSquareAtCell(
                          TileMap.GetCellByPixelX((int)mouseLoc.X),
                          TileMap.GetCellByPixelY((int)mouseLoc.Y)
                        ).TogglePassable();
                    }
                }

                HoverCodeValue =
                        TileMap.GetMapSquareAtCell(
                            TileMap.GetCellByPixelX(
                                (int)mouseLoc.X),
                            TileMap.GetCellByPixelY(
                                (int)mouseLoc.Y)).CodeValue;
            }
        }

        lastMouseState = ms;

        base.Update(gameTime);
    }

    /// <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)
    {
        GraphicsDevice.Clear(Color.Black);

        spriteBatch.Begin(
            SpriteSortMode.BackToFront,
            BlendState.AlphaBlend);
        TileMap.Draw(spriteBatch);
        spriteBatch.End();

        base.Draw(gameTime);
    }    
  }
}

Thanks!

\$\endgroup\$

1 Answer 1

1
\$\begingroup\$

I have ran into the same trouble with that same book and I believe it is because that book is using true XNA while you are using the modern non-microsoft supported MonoGame. I think winForms has changed in some regard since the release of this book.

The only fix I could figure out was to change this

public Game1(IntPtr drawSurface,
        System.Windows.Forms.Form parentForm,
        System.Windows.Forms.PictureBox surfacePictureBox)
{
    graphics = new GraphicsDeviceManager(this);
    Content.RootDirectory = "Content";

    this.drawSurface = drawSurface;
    this.parentForm = parentForm;
    this.pictureBox = surfacePictureBox;

    graphics.PreparingDeviceSettings +=
    new EventHandler<PreparingDeviceSettingsEventArgs>(
    graphics_PreparingDeviceSettings);

    Mouse.WindowHandle = drawSurface;

    gameForm = 
    System.Windows.Forms.Control.FromHandle(this.Window.Handle);

    gameForm.VisibleChanged += new EventHandler(gameForm_VisibleChanged);

    pictureBox.SizeChanged += new EventHandler(pictureBox_SizeChanged);

    vscroll =
    (System.Windows.Forms.VScrollBar)parentForm.Controls["vScrollBar1"];
    hscroll =
    (System.Windows.Forms.HScrollBar)parentForm.Controls["hScrollBar1"];

    IsMouseVisible = true;
}

to something like

enter image description here

Unfortunately, this will not solve your problem but will only make it so your game is on a seperate window than the forms control... so you will have two windows.

An approach I've seen is to somehow make your xna drawing window into a forms control, and then use that control like any other forms tool. This can be easily implemented by using something like this MonoGame.Forms. MonoGame.Forms does all that for you and I have personally found useful for making editor environments for XNA, and will most likely solve your problem.

\$\endgroup\$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .