1

我正在尝试制作一个滑块益智游戏,当我在 form1 中调用 myBoard.paint(e.Graphics) 时,我不断收到错误“NullReferenceException 未处理”。请帮我!!!

这是我的 Form1 代码(如果我需要发布一些其他类的代码,请告诉我):

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Diagnostics;

namespace SliderPuzzle
{
    public partial class Form1 : Form
    {
        private int tileSize;
        private int rowsCols;
        private SlidePuzzle myBoard;
        private Stopwatch timer;
        private int moveCount;

        public Form1()
        {
            InitializeComponent();
            pictureBox1.TabIndex = 3;
            pictureBox1.Size = new Size(100, 50);
            pictureBox1.Location = new Point(16, 71);
            pictureBox1.BackColor = Color.PaleGreen;
            pictureBox1.BorderStyle = BorderStyle.Fixed3D;
            pictureBox1.TabStop = false;
            tileSize = imageList1.ImageSize.Width;
            rowsCols = 3;
            pictureBox1.Width = rowsCols * tileSize;
            pictureBox1.Height = rowsCols * tileSize;
        }

        public void initGame()
        {
            myBoard = new SlidePuzzle(rowsCols, tileSize, imageList1);
            timer = new Stopwatch();
            moveCount = 0;
            timer.Start();
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            initGame();
        }

        private void pictureBox1_Paint(object sender, PaintEventArgs e)
        {
          this.myBoard.paint(e.Graphics);
        }

        private void pictureBox1_MouseDown(object sender, MouseEventArgs e)
        {
            if (myBoard.move(e.Y / tileSize, e.X / tileSize))
                ++moveCount;
            Refresh();
            if (!myBoard.winner())
                return;
            timer.Stop();
            if (MessageBox.Show(string.Format("You won!!\nIt took you {0} moves and {1:F2} seconds.\nPlay again?", (object)moveCount, (object)timer.Elapsed.TotalSeconds), "Game Over", MessageBoxButtons.YesNo, MessageBoxIcon.Exclamation) == DialogResult.No)
            {
                Close();
            }
            else
            {
                initGame();
                Refresh();
            }
        }
    }
}

更新 #1:好的,所以我移动了 myBoard = new SlidePuzzle(rowsCols, tileSize, imageList1); 到我的构造函数,但现在没有图像显示在上面。这是它的样子与它应该看起来的样子:在此处输入图像描述

编辑#2:好的,我把它移回原来的位置并放

if (this.myBoard != null)
        this.myBoard.paint(e.Graphics);

相反,它工作得更好一些,看起来也更好。但是图像不显示仍然是一个问题。

编辑#3:这是 SliderPuzzle.Paint 代码:

        public void paint(Graphics g)
    {
        for (int r = 0; r < this.myGrid.getNumRows(); ++r)
        {
            for (int c = 0; c < this.myGrid.getNumCols(); ++c)
                this.myGrid.get(new Location(r, c)).paint(g);
        }
    }

编辑#4:这是 SliderPuzzle 类的代码:

using System;
using System.Drawing;
using System.Windows.Forms;

namespace SliderPuzzle
{
    internal class SlidePuzzle
    {
        private static Random rand = new Random();
        private int myTileSize;
        private BoundedGrid myGrid;
        private ImageList myImages;
        private Location myBlankLoc;

        static SlidePuzzle()
        {
        }

        public SlidePuzzle(int rowsCols, int tileSize, ImageList images)
        {
            this.myTileSize = tileSize;
            this.myGrid = new BoundedGrid(rowsCols, rowsCols);
            this.myImages = images;
            this.myBlankLoc = new Location(rowsCols - 1, rowsCols - 1);
            this.initBoard();
        }

        private void initBoard()
        {
            int index1 = 0;
            for (int r = 0; r < this.myGrid.getNumRows(); ++r)
            {
                for (int c = 0; c < this.myGrid.getNumCols(); ++c)
                {
                    this.myGrid.put(new Location(r, c), new Tile(index1, this.myTileSize, new Location(r, c), this.myImages.Images[index1]));
                    ++index1;
                }
            }
            for (int index2 = 0; index2 < 1000; ++index2)
            {
                Location adjacentLocation = this.myBlankLoc.getAdjacentLocation(SlidePuzzle.rand.Next(4) * 90);
                if (this.myGrid.isValid(adjacentLocation))
                {
                    this.swap(this.myBlankLoc, adjacentLocation);
                    this.myBlankLoc = adjacentLocation;
                }
            }
        }

        public bool move(int row, int col)
        {
            Location loc1 = new Location(row, col);
            if (Math.Abs(this.myBlankLoc.getRow() - row) + Math.Abs(this.myBlankLoc.getCol() - col) != 1)
                return false;
            this.swap(loc1, this.myBlankLoc);
            this.myBlankLoc = loc1;
            return true;
        }

        public bool winner()
        {
            int num = 0;
            for (int r = 0; r < this.myGrid.getNumRows(); ++r)
            {
                for (int c = 0; c < this.myGrid.getNumCols(); ++c)
                {
                    if (this.myGrid.get(new Location(r, c)).getValue() != num)
                        return false;
                    ++num;
                }
            }
            return true;
        }

        private void swap(Location loc1, Location loc2)
        {
            Tile tile1 = this.myGrid.put(loc2, this.myGrid.get(loc1));
            Tile tile2 = this.myGrid.put(loc1, tile1);
            tile1.setLocation(loc1);
            tile2.setLocation(loc2);
        }

        public void paint(Graphics g)
        {
            for (int r = 0; r < this.myGrid.getNumRows(); ++r)
            {
                for (int c = 0; c < this.myGrid.getNumCols(); ++c)
                    this.myGrid.get(new Location(r, c)).paint(g);
            }
        }
    }
}

更新 #5:这是 Tile 类:

using System.Drawing;

namespace SliderPuzzle
{
    internal class Tile
    {
        private int myValue;
        private int mySize;
        private Location myLoc;
        private Image myImage;

        public Tile(int value, int tileSize, Location loc, Image img)
        {
            this.myValue = value;
            this.mySize = tileSize;
            this.myLoc = loc;
            this.myImage = img;
        }

        public int getValue()
        {
            return this.myValue;
        }

        public void setLocation(Location newLoc)
        {
            this.myLoc = newLoc;
        }

        public void paint(Graphics g)
        {
            g.DrawImage(this.myImage, this.myLoc.getCol() * this.mySize, this.myLoc.getRow() * this.mySize);
        }
    }
}

编辑#6:这是位置类:

namespace SliderPuzzle
{
    internal class Location
    {
        public const int LEFT = -90;
        public const int RIGHT = 90;
        public const int HALF_LEFT = -45;
        public const int HALF_RIGHT = 45;
        public const int FULL_CIRCLE = 360;
        public const int HALF_CIRCLE = 180;
        public const int AHEAD = 0;
        public const int NORTH = 0;
        public const int NORTHEAST = 45;
        public const int EAST = 90;
        public const int SOUTHEAST = 135;
        public const int SOUTH = 180;
        public const int SOUTHWEST = 225;
        public const int WEST = 270;
        public const int NORTHWEST = 315;
        private int row;
        private int col;

        public Location(int r, int c)
        {
            this.row = r;
            this.col = c;
        }

        public int getRow()
        {
            return this.row;
        }

        public int getCol()
        {
            return this.col;
        }

        public Location getAdjacentLocation(int direction)
        {
            int num1 = (direction + 22) % 360;
            if (num1 < 0)
                num1 += 360;
            int num2 = num1 / 45 * 45;
            int num3 = 0;
            int num4 = 0;
            if (num2 == 90)
                num3 = 1;
            else if (num2 == 135)
            {
                num3 = 1;
                num4 = 1;
            }
            else if (num2 == 180)
                num4 = 1;
            else if (num2 == 225)
            {
                num3 = -1;
                num4 = 1;
            }
            else if (num2 == 270)
                num3 = -1;
            else if (num2 == 315)
            {
                num3 = -1;
                num4 = -1;
            }
            else if (num2 == 0)
                num4 = -1;
            else if (num2 == 45)
            {
                num3 = 1;
                num4 = -1;
            }
            return new Location(this.getRow() + num4, this.getCol() + num3);
        }

        public bool equals(Location other)
        {
            if (this.getRow() == other.getRow())
                return this.getCol() == other.getCol();
            else
                return false;
        }

        public int hashCode()
        {
            return this.getRow() * 3737 + this.getCol();
        }

        public int compareTo(Location otherLoc)
        {
            if (this.getRow() < otherLoc.getRow())
                return -1;
            if (this.getRow() > otherLoc.getRow())
                return 1;
            if (this.getCol() < otherLoc.getCol())
                return -1;
            return this.getCol() > otherLoc.getCol() ? 1 : 0;
        }

        public string toString()
        {
            return "(" + (object)this.getRow() + ", " + (string)(object)this.getCol() + ")";
        }
    }
}

编辑#7:这是最后一个类,BoundedGrid 类:

using System;
using System.Collections.Generic;

namespace SliderPuzzle
{
    internal class BoundedGrid
    {
        private Tile[,] occupantArray;

        public BoundedGrid(int rows, int cols)
        {
            this.occupantArray = new Tile[rows, cols];
        }

        public int getNumRows()
        {
            return this.occupantArray.GetLength(0);
        }

        public int getNumCols()
        {
            return this.occupantArray.GetLength(1);
        }

        public bool isValid(Location loc)
        {
            if (0 <= loc.getRow() && loc.getRow() < this.getNumRows() && 0 <= loc.getCol())
                return loc.getCol() < this.getNumCols();
            else
                return false;
        }

        public List<Location> getOccupiedLocations()
        {
            List<Location> list = new List<Location>();
            for (int r = 0; r < this.getNumRows(); ++r)
            {
                for (int c = 0; c < this.getNumCols(); ++c)
                {
                    Location loc = new Location(r, c);
                    if (this.get(loc) != null)
                        list.Add(loc);
                }
            }
            return list;
        }

        public Tile get(Location loc)
        {
            if (!this.isValid(loc))
                throw new Exception("Location " + (object)loc + " is not valid");
            else
                return this.occupantArray[loc.getRow(), loc.getCol()];
        }

        public Tile put(Location loc, Tile obj)
        {
            if (!this.isValid(loc))
                throw new Exception("Location " + (object)loc + " is not valid");
            if (obj == null)
                throw new NullReferenceException("obj == null");
            Tile tile = this.get(loc);
            this.occupantArray[loc.getRow(), loc.getCol()] = obj;
            return tile;
        }

        public Tile remove(Location loc)
        {
            if (!this.isValid(loc))
                throw new Exception("Location " + (object)loc + " is not valid");
            Tile tile = this.get(loc);
            this.occupantArray[loc.getRow(), loc.getCol()] = (Tile)null;
            return tile;
        }
    }
}

编辑#8:当我点击图片框时,程序崩溃并显示 timer.Stop(); 在 form1 中给了我一个 NullReferenceException !!!

编辑#9:好的,这有效......我发现图像仍然没有出现,但我认为它们永远不会被放置在网格上。当我点击网格时(仍然没有图像)它说我赢了。这应该只在我将瓷砖移动到正确的顺序后才会显示。知道发生了什么吗?

编辑#10:我的程序现在终于可以工作了!原来我在 form1 的构造函数中放错了地方,现在一切正常!图像显示和一切!多么酷啊!!!

谢谢大家的贡献,我现在将在我的学校项目中获得高分!

4

3 回答 3

3

这是让您头疼的问题之一。尝试确定事件的确切顺序很好,因为您可以保证事件永远不会被乱序调用。不幸的是,该Paint事件是在各种奇怪的时间被触发的事件,甚至在Load事件之前都可能被触发。

唯一真正的答案是永远不要依赖以特定顺序触发的事件。myBoard在尝试绘制之前,请务必检查您的对象是否有效:

private void pictureBox1_Paint(object sender, PaintEventArgs e)
{
    if (this.myBoard != null)
        this.myBoard.paint(e.Graphics);
}

是的,正如其他人所指出的,最好尽快创建所有对象 - 例如在构造函数中,毕竟这是构造函数的用途。即使这样,您仍然可以通过构造函数中的操作触发事件,这会破坏您的一整天。您的偶数处理程序应在构造函数代码中尽可能晚地设置以解决此问题。

经验法则:

  • 如果您正在创建将在事件处理程序中使用的自定义对象,请始终尝试在调用InitializeComponent构造函数之前创建和初始化它们。

  • 尽可能晚地连接你的事件处理程序,特别是对于像这样的事情Paint- 在你的构造函数完成之前它不会有用,所以在构造函数中最后做。

于 2013-09-11T22:54:15.137 回答
2

您的实际Form1_Load事件永远不会被调用(您正在初始化电路板的事件)。在 Form1 构造函数的末尾添加此代码this.Load +=Form1_Load;

于 2013-09-11T23:58:34.993 回答
-1

您的pictureBox1.Paint事件在您的事件之前提出Form1.Load。移动

myBoard = new SlidePuzzle(rowsCols, tileSize, imageList1);

给你的构造函数,你应该没问题。

于 2013-09-11T22:49:04.067 回答