1

Я пытаюсь сделать головоломку слайдера, и я продолжаю получать ошибку «NullReferenceException был необработанным», когда я вызываю myBoard.paint (e.Graphics) в моей форме1. Пожалуйста, помогите мне!!!Почему возникает ошибка «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(); 
      } 
     } 
    } 
} 

Update # 1: Хорошо, так что я переехал myBoard = новые SlidePuzzle (rowsCols, tileSize, imageList1); к моему конструктору, но теперь ни одно из изображений не появляется на нем. Вот что он выглядит как против того, что он должен выглядеть следующим образом: enter image description here

Edit # 2: Хорошо, я перенес ее туда, где она была раньше, и поставил

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

вместо этого, и это работает немного лучше и выглядит лучше. Но изображения, которые не отображаются, по-прежнему остаются проблемой.

Edit # 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); 
      } 
     } 
    } 
} 

Update # 5: Вот Плитка Класс:

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() + ")"; 
     } 
    } 
} 

Edit # 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; 
     } 
    } 
} 

Edit # 8: Когда я нажимаю на PictureBox, программа падает, и это говорит о timer.Stop (); в form1 дает мне исключение NullReference !!!

Редактировать # 9: Хорошо, это сработало ... Я обнаружил, что изображения все еще не отображаются, но я думаю, что они никогда не помещаются в сетку. Когда я нажимаю на сетку (по-прежнему нет изображений), она говорит, что я выиграл. Это должно отображаться только после перемещения плитки в правильном порядке. Любая идея, что происходит?

Редактировать # 10: Моя программа, наконец, работает сейчас! Оказывается, у меня было что-то упущенное в конструкторе form1, теперь все работает! Изображения появляются и все! Как это круто!!!

СПАСИБО ВАС ВСЕ ДЛЯ ВАС ВЗНОСЫ, Я ПОЛУЧАЮ БОЛЬШУЮ СТРАНИЦУ НА МОЙ ШКОЛЬНЫЙ ПРОЕКТ!

+1

@Silvermind Нет .. он этого не сделал. ('initGame', вызываемый из' Form_Load'). –

+0

@SimonWhitehead Да, он сделал это, потому что он вызывает 'InitializeComponent' перед' initGame', который запускает 'Paint'. – Silvermind

+0

@Silvermind Нет, нет. Попробуй. –

ответ

-1

Ваше мероприятие pictureBox1.Paint возбуждено перед вашим мероприятием Form1.Load. Переведите

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

Вашему конструктору и все должно быть хорошо.

+1

Вы пробовали это? Потому что он работает .. без этого ('Load' вызывается до того, как его элементы управления вызывают события' Paint'). –

+0

@SimonWhitehead В обработчике 'Paint' он получает« NullReferenceException », но после переноса инициализации' myBoard' на конструктор вместо обработчика 'Load' он больше не работает (см. Обновление). Однако вы утверждаете, что обработчик 'Paint' не был вызван перед обработчиком' Load'? В этом нет смысла. –

+0

Нет 'NullRef' .. но и ничего другого. Я подозреваю, что это намного больше, чем нам показывают. –

3

Это одна из тех проблем, которая даст вам головные боли. Попытка сгладить точную последовательность событий прекрасна, когда вы можете гарантировать, что события никогда не будут вызываться из последовательности.К сожалению, событие Paint - это тот, который уволен во всевозможные нечетные времена, любой может быть запущен еще до события Load.

Единственный реальный ответ на никогда не полагаться на события увольняют в определенной последовательности. Всегда убедитесь, что ваш myBoard объект действителен, прежде чем пытаться нарисовать его:

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

И да, как отметили другие, то лучше создать все объекты как можно скорее - в конструкторе, например, что это конструктор для в конце концов. Даже тогда вы все равно можете получать события, вызванные действиями в конструкторе, которые разрушат весь ваш день. Ваши четные обработчики должны быть установлены как можно скорее в коде конструктора, чтобы учесть это.

Rule (s) эмпирическое:

  • , если вы создаете пользовательские объекты, которые будут использоваться в обработчики событий, всегда пытаются создать и инициализировать их перед вызовом InitializeComponent в конструкторе.

  • Подключите обработчики событий как можно дольше, особенно для таких вещей, как Paint. Это не будет полезно, прежде чем ваш конструктор будет выполнен, так что это будет продолжаться в конструкторе.

+1

Я поддерживаю это, потому что это дает практическое обходное решение .. однако, 'Paint' не называется _before_' Load' .. он вызывается после таких событий, как 'Layout',' Activated' и 'VisibleChanged'. Что имеет смысл. Как дочерний элемент управления может вызвать событие «Paint», когда родительская форма даже не отображает его содержимое? –

+1

@SimonWhitehead Поскольку 'Paint' не *** ***, вызванный стандартным потоком событий, он вызван во всевозможные случайные моменты по разному коду, который мы не контролируем. Это большая боль в заднице, но «Краска» - худший преступник. И да, это ** делает ** вызывается перед загрузкой в ​​некоторых случаях. – Corey

+1

Можете ли вы связать меня с источниками, где говорится, что Кори? Я не могу найти (и с радостью буду есть мои слова, если это так!). Помня о том, что событие «Paint» подключено к элементу управления _child_, а не к событию 'Paint' самой формы. –

2

Ваше фактическое событие Form1_Load никогда не вызывается (тот, где вы инициализируете свою доску). Добавить этот код в конец конструктора Form1 this.Load +=Form1_Load;

+0

Хорошо, ЭТО работает ... Я обнаружил, что изображения все еще не отображаются, но я думаю, что они никогда не помещаются в сетку. Когда я нажимаю на сетку (по-прежнему нет изображений), она говорит, что я выиграл. Это должно отображаться только после перемещения плитки в правильном порядке. Любая идея, что происходит? –

+0

@DrewStauft Что касается изображений, вы уверены, что все они добавлены в ImageList? Кроме того, возможно, вы установите их все на пустой, когда вы перетасовываете их. Проверьте логику вашего приложения. –

+0

@DrewStauft Это совершенно другая проблема. Если вы не можете понять это сами, не стесняйтесь возвращаться к SO, отправляя эту конкретную проблему (вместе с соответствующим кодом) в качестве нового вопроса. –

Смежные вопросы