C# Newb can't figure out why drawing blue square in a ui class throws null reference exception

Hi All - I’m very new to C# (4 yrs experience with python which is very different). I’m attempting to create a feature that creates a square where I click the mouse.

It throws the error whenever I un-comment this line from my UI class: Globals.spriteBatch.Draw(pixel, blueSquareTop, blueSquareColor);

Error:

An unhandled exception of type 'System.ArgumentNullException' occurred in MonoGame.Framework.dll
Value cannot be null.

UI Class

#region Includes
using System;
using System.Collections.Generic;
using System.Linq;
using System.Xml.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;

using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
using ProjectBeta.Source.GamePlay.World;
using System.Diagnostics;
#endregion

namespace ProjectBeta
{
    public class UI
    {
        public World world;
        public SpriteFont font;
        SpriteBatch spriteBatch;
        public GraphicsDevice _graphicsDevice;
        Vector2 coor;

        //Rectangles
        Texture2D pixel;
        Rectangle blueSquareTop;
        Rectangle blueSquareLeft;
        Rectangle blueSquareRight;
        Rectangle blueSquareBottom;
        Color blueSquareColor;
        //Selection Square Logic for UI
        Vector2 squareTopLeft;
        float squareSide;
        float squareThickness;
        bool selectSquarePressed;

        public UI(GraphicsDevice GRAPHICSDEVICE) 
        {
            _graphicsDevice = GRAPHICSDEVICE;
            font = Globals.content.Load<SpriteFont>("fonts\\File");

        }

        public void LoadContent(GraphicsDevice GRAPHICSDEVICE)
        {
            /// Create the single-pixel texture
            pixel = new Texture2D(GRAPHICSDEVICE, 1, 1);
            pixel.SetData<Color>(new Color[] { Color.White });

            //Create the rectangle
            squareTopLeft = new Vector2(600, 50);
            squareSide = 150.0f;
            squareThickness = 2.0f;

            blueSquareTop = new Rectangle((int)(squareTopLeft.X), (int)(squareTopLeft.Y), (int)(squareSide), (int)(squareThickness));
            blueSquareLeft = new Rectangle((int)(squareTopLeft.X), (int)(squareTopLeft.Y), (int)(squareThickness), (int)(squareSide));
            //blueSquareBottom = new Rectangle((int)(squareTopLeft.X), (int)(squareTopLeft.Y)+(int)(squareSide), (int)(squareSide), (int)(squareThickness));
            blueSquareBottom = new Rectangle((int)(squareTopLeft.X), (int)(squareTopLeft.Y) + (int)(squareSide), (int)(squareSide), (int)(squareThickness));
            blueSquareRight = new Rectangle((int)(squareTopLeft.X) + (int)(squareSide), (int)(squareTopLeft.Y), (int)(squareThickness), (int)(squareSide));
        }

        public void UnloadContent() 
        {
            //selectionSquare.Dispose();
        }

        public void Update(World WORLD)
        {
            //selectSquarePos = new Vector2(-1f, -1f);
            //if (Globals.mouse.LeftClick()) 
            //{
            //    selectSquarePos = new Vector2(Globals.mouse.newMousePos.X, Globals.mouse.newMousePos.Y);
            //}
        }

        public void Draw(World WORLD, GraphicsDevice GRAPHICSDEVICE)
        {
            //Globals.spriteBatch.Begin();
            //SPRITEBATCH.Begin();
            //Drawing heads up UI
            string tempStr = WORLD.UiText;
            //string tempStr = "World, Branches, Trees, Sun, creatures";
            Vector2 strDims = font.MeasureString(tempStr);
            //Globals.spriteBatch.DrawString(font, tempStr, new Vector2(Globals.screenWidth/2, 50), Color.Black);
            //Globals.spriteBatch.DrawString(font, new string((Globals.screenWidth+2).ToString()), new Vector2(0, 0), Color.Black);

            //Drawing mouse click selection square
            //Globals.spriteBatch.DrawString(font, tempStr, new Vector2(0,0), Color.Black);

            Globals.spriteBatch.DrawString(font, tempStr, new Vector2(Globals.screenWidth / 2, 50), Color.Black);

            //Selection Square logic
            //if (selectSquarePressed)
            //{
            squareTopLeft = Globals.mouse.newMousePos;
            blueSquareTop = new Rectangle((int)(squareTopLeft.X), (int)(squareTopLeft.Y), (int)(squareSide), (int)(squareThickness));
            blueSquareLeft = new Rectangle((int)(squareTopLeft.X), (int)(squareTopLeft.Y), (int)(squareThickness), (int)(squareSide));
            //blueSquareBottom = new Rectangle((int)(squareTopLeft.X), (int)(squareTopLeft.Y)+(int)(squareSide), (int)(squareSide), (int)(squareThickness));
            blueSquareBottom = new Rectangle((int)(squareTopLeft.X), (int)(squareTopLeft.Y) + (int)(squareSide), (int)(squareSide), (int)(squareThickness));
            blueSquareRight = new Rectangle((int)(squareTopLeft.X) + (int)(squareSide), (int)(squareTopLeft.Y), (int)(squareThickness), (int)(squareSide));
            selectSquarePressed = false;
            Globals.spriteBatch.DrawString(font, "TopLeft X: " + squareTopLeft.X.ToString(), new Vector2(50, 100), Color.Black);
            Globals.spriteBatch.DrawString(font, "TopLeft Y: " + squareTopLeft.Y.ToString(), new Vector2(50, 130), Color.Black);
            Globals.spriteBatch.Draw(pixel, blueSquareTop, blueSquareColor);
            //Globals.spriteBatch.Draw(pixel, blueSquareLeft, Color.Red);
            //Globals.spriteBatch.Draw(pixel, blueSquareBottom, Color.Green);
            //Globals.spriteBatch.Draw(pixel, blueSquareRight, Color.Beige);

        }
    }
}

World Class

#region Includes
using System;
using System.Collections.Generic;
using System.Linq;
using System.Xml.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;

using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
#endregion

namespace ProjectBeta.Source.GamePlay.World
{
    public class World
    {
        public Vector2 offset;

        public Hero hero;

        public UI ui;

        public string UiText;

        private GraphicsDevice _graphicsDevice;

        //public SquareGrid grid;

        public World(GraphicsDevice GRAPHICSDEVICE)
        {

            hero = new Hero("2D\\minifig", new Vector2(300, 300), new Vector2(48, 48));
            //grid = new SquareGrid(new Vector2(25,25), new Vector2(-100,-100), new Vector2(Globals.screenWidth+200, Globals.screenHeight+200));

            //UI Text and instantiation
            UiText = "Roots, branches, trees, and creatures";
            _graphicsDevice = GRAPHICSDEVICE;
            ui = new UI(_graphicsDevice);
            
        }

        public void LoadContent()
        {
            ui.LoadContent(this._graphicsDevice);
        }

        public void UnloadContent()
        {
            ui.UnloadContent();
        }

        public virtual void Update()
        {

            hero.Update();

            ui.Update(this);
        }

        public virtual void Draw(Vector2 OFFSET, GraphicsDevice GRAPHICSDEVICE)
        {
            hero.Draw(OFFSET);

            ui.Draw(this, GRAPHICSDEVICE);
        }
    }
}

Base Game class

#region Includes
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Xml.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;

using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
using ProjectBeta.Source.GamePlay.World;
#endregion

namespace ProjectBeta
{
    public class BetaG : Game
    {
        World world;
        //Texture2D ballTexture;
        //Texture2D miniFig;
        //Vector2 ballPosition;
        //float ballSpeed;
        //float ballRotate;
        //float rotateSpeed;

        private GraphicsDeviceManager graphics;
        private SpriteBatch spriteBatch;

        Basic2d cursor;
        private BasicEffect _basicEffect;

        public BetaG()
        {
            graphics = new GraphicsDeviceManager(this);
            Content.RootDirectory = "Content";
            IsMouseVisible = false;

            //Create the blue square
            //int x = 10;
            //int y = 10;
            //int width = 20;
            //int height = 20;
            //Rectangle rectangle = new Rectangle(x, y, width, height);

        }

        protected override void Initialize()
        {
            // TODO: Add your initialization logic here
            //ballPosition = new Vector2(graphics.PreferredBackBufferWidth / 2, graphics.PreferredBackBufferHeight / 2);
            //ballSpeed = 50f;
            //ballRotate = 0f;
            //rotateSpeed = 10;
            
            base.Initialize();
        }

        protected override void LoadContent()
        {
            Globals.content = this.Content;
            Globals.spriteBatch = new SpriteBatch(GraphicsDevice);

            // TODO: use this.Content to load your game content here
            cursor = new Basic2d("2D\\Cursor2D\\cursor1", new Vector2(0,0), new Vector2(28,28));
            //cursor = Content.Load<Texture2D>("cursor1");
            //ballTexture = Content.Load<Texture2D>("ball");
            Globals.keyboard = new InpKeyboard();
            Globals.mouse = new InpMouseControl();

            world = new World(GraphicsDevice);

            //creating rectangle



        }

        protected override void UnloadContent()
        {
            base.UnloadContent();
            world.UnloadContent();
        }

        protected override void Update(GameTime gameTime)
        {
            if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed || Keyboard.GetState().IsKeyDown(Keys.Escape))
                Exit();

            // TODO: Add your update logic here
            Globals.keyboard.Update();
            Globals.mouse.Update();



            world.Update();
            //Includes: Old Ball logic
            #region Includes
            //var kstate = Keyboard.GetState();
            //
            //if (kstate.IsKeyDown(Keys.Up))
            //{
            //    ballPosition.Y -= ballSpeed * (float)gameTime.ElapsedGameTime.TotalSeconds;
            //}
            //
            //if (kstate.IsKeyDown(Keys.Down))
            //{
            //    ballPosition.Y += ballSpeed * (float)gameTime.ElapsedGameTime.TotalSeconds;
            //}
            //
            //if (kstate.IsKeyDown(Keys.Left))
            //{
            //    ballPosition.X -= ballSpeed * (float)gameTime.ElapsedGameTime.TotalSeconds;
            //    //ballRotate -= rotateSpeed * (float)gameTime.ElapsedGameTime.TotalSeconds;
            //}
            //
            //if (kstate.IsKeyDown(Keys.Right))
            //{
            //    ballPosition.X += ballSpeed * (float)gameTime.ElapsedGameTime.TotalSeconds;
            //}
            //
            //if (ballPosition.X > graphics.PreferredBackBufferWidth - ballTexture.Width / 2)
            //{
            //    ballPosition.X = graphics.PreferredBackBufferWidth - ballTexture.Width / 2;
            //}
            //else if (ballPosition.X < ballTexture.Width / 2)
            //{
            //    ballPosition.X = ballTexture.Width / 2;
            //}
            //
            //if (ballPosition.Y > graphics.PreferredBackBufferHeight - ballTexture.Height / 2)
            //{
            //    ballPosition.Y = graphics.PreferredBackBufferHeight - ballTexture.Height / 2;
            //}
            //else if (ballPosition.Y < ballTexture.Height / 2)
            //{
            //    ballPosition.Y = ballTexture.Height / 2;
            //}
            #endregion
            Globals.keyboard.UpdateOld();
            Globals.mouse.UpdateOld();

            base.Update(gameTime);

        }

        protected override void Draw(GameTime gameTime)
        {
            GraphicsDevice.Clear(Color.CornflowerBlue);


            // TODO: Add your drawing code here
            Globals.spriteBatch.Begin();


            world.Draw(new Vector2(1,1), GraphicsDevice);

            if (Globals.mouse.dragging == true)
            {
                //Globals.spriteBatch.DrawString(Globals.content.Load<SpriteFont>("fonts\\File"), "dragging", new Vector2(50, 50), Color.Black);
                //below line bugged with a null ref exception
                Globals.spriteBatch.DrawString(world.ui.font, "dragging", new Vector2(650, 50), Color.Black);

            }

            //Draw sword pixel as cursor
            cursor.Draw(new Vector2(Globals.mouse.newMousePos.X, Globals.mouse.newMousePos.Y), new Vector2(0, 0));

            Globals.spriteBatch.End();

            base.Draw(gameTime);
        }
    }
}

(Also, glad to share in a different place if that’s easier; just seemed like this forum was a good place to seek help)

I think you need to call world.LoadContent(GraphicsDevice); in your BetaG class’s LoadContent() function.

This is a modified version of your LoadContent function in your game class:


        protected override void LoadContent()
        {
            Globals.content = this.Content;
            Globals.spriteBatch = new SpriteBatch(GraphicsDevice);

            // TODO: use this.Content to load your game content here
            cursor = new Basic2d("2D\\Cursor2D\\cursor1", new Vector2(0,0), new Vector2(28,28));
            //cursor = Content.Load<Texture2D>("cursor1");
            //ballTexture = Content.Load<Texture2D>("ball");
            Globals.keyboard = new InpKeyboard();
            Globals.mouse = new InpMouseControl();

            world = new World(GraphicsDevice);
            world.LoadContent(GraphicsDevice); // <-- add this line
        }

Thank you - that fixed the error! Would you mind explaining how you knew that was the issue?

Hey Jeff! The issue was the LoadContent() function in your UI class appears to be responsible for initializing a single pixel texture.
In particular, this line here: pixel = new Texture2D(GRAPHICSDEVICE, 1, 1);
Notice how you are passing in the local GRAPHICSDEVICE variable to create the Texture2D?

The function is written correctly I think, but you never called the LoadContent(GRAPHICSDEVICE) function anywhere to initialize the single pixel texture. And if you don’t know, in C#, the default value of object types (object is loosely the same as variables with class types like string, Texture2D, SpriteBatch etc.) is null, meaning that your pixel field in the UI class is null by default until you make it equal to something non-null. Your LoadContent(GRAPHICSDEVICE) function in your UI class correctly sets it to a valid texture, but in your original code, you never called the UI class’s LoadContent(GRAPHICSDEVICE) after you created an instance of UI.

Anyways, hopefully that clears it up!

Here’s a side note: I noticed that in the constructor of your UI class, you stored a reference to a graphics device in a field called _graphicsDevice (i.e., a class variable like self._graphicsDevice in Python), meaning that your LoadContent function in your UI class doesn’t actually need any parameters in the given code. But instead, you can write it like this:

 public void LoadContent() // <- no parameter needed, you have a variable with the same graphics device
        {
            /// Create the single-pixel texture
            pixel = new Texture2D(_graphicsDevice, 1, 1); <- use field instead
            pixel.SetData<Color>(new Color[] { Color.White });

            //Create the rectangle
            squareTopLeft = new Vector2(600, 50);
            squareSide = 150.0f;
            squareThickness = 2.0f;

            blueSquareTop = new Rectangle((int)(squareTopLeft.X), (int)(squareTopLeft.Y), (int)(squareSide), (int)(squareThickness));
            blueSquareLeft = new Rectangle((int)(squareTopLeft.X), (int)(squareTopLeft.Y), (int)(squareThickness), (int)(squareSide));
            //blueSquareBottom = new Rectangle((int)(squareTopLeft.X), (int)(squareTopLeft.Y)+(int)(squareSide), (int)(squareSide), (int)(squareThickness));
            blueSquareBottom = new Rectangle((int)(squareTopLeft.X), (int)(squareTopLeft.Y) + (int)(squareSide), (int)(squareSide), (int)(squareThickness));
            blueSquareRight = new Rectangle((int)(squareTopLeft.X) + (int)(squareSide), (int)(squareTopLeft.Y), (int)(squareThickness), (int)(squareSide));
        }

But that’s just my personal suggestion, there’s nothing else wrong with the code in terms of null reference exceptions.

1 Like

Thanks Tommy - Very helpful!