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
\$\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\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.