Continuing with our game, all that is left to do is to add the physical Maze object and enable it to work with the existing Grid and Game classes and add it to the form. In this part, you will create the maze object. If you haven’t completed “Creating a Maze Game in .NET, Part 1: Structure” yet, please do so before beginning this article.
Let’s jump right in.
Add a new class and name it Maze, for example.
Add the following code for this class:
C#
using System; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Security; using System.Text; using System.Threading.Tasks; using Microsoft.VisualBasic; using System.Windows.Forms; using System.Drawing; namespace HTG_Maze_VB { class Maze : DataGridView { private int WM_LBUTTONDOWN = 0x201; private int WM_LBUTTONDBLCLK = 0x203; private int WM_KEYDOWN = 0x100; public Maze() { this.DoubleBuffered = true; } private bool blnSolution; public bool ShowSolution { get { return blnSolution; } set { blnSolution = value; } } protected override void OnRowPrePaint(System.Windows. .DataGridViewRowPrePaintEventArgs e) { e.PaintParts = e.PaintParts & ~DataGridViewPaintParts.Focus; base.OnRowPrePaint(e); } protected override void OnPaint(System.Windows.Forms .PaintEventArgs e) { base.OnPaint(e); if (clsGrid.Cells == null) return; for (int row = 0; row <= base.Rows.Count - 1; row++) { for (int col = 0; col <= base.Columns.Count - 1; col++) { int intWidth = 2; Pen pRed = new Pen(Color.Red, intWidth); Pen pGreen = new Pen(Color.Green, intWidth); int intSize = base.Rows[0].Cells[0].Size.Width; if (ShowSolution ? Convert.ToBoolean (clsGrid.Solution[col][row]) : Convert.ToBoolean(clsGrid.Cells[col][row] .intNorth > 0)) e.Graphics.DrawLine(ShowSolution ? pGreen : pRed, System.Convert.ToInt32(intSize * col + ((intSize - intWidth) / (double)2) + 1), System.Convert.ToInt32(intSize * row), System.Convert.ToInt32(intSize * col + ((intSize - intWidth) / (double)2) + 1), System.Convert.ToInt32(intSize * row + ((intSize - intWidth) / (double)2) + 1)); if (ShowSolution ? Convert.ToBoolean(clsGrid .Solution[col][row]) : Convert.ToBoolean (clsGrid.Cells[col][row].intSouth > 0)) e.Graphics.DrawLine(ShowSolution ? pGreen : pRed, System.Convert.ToInt32(intSize * col + ((intSize - intWidth) / (double)2) + 1), System.Convert.ToInt32(intSize * row + ((intSize - intWidth) / (double)2) + 1), System.Convert.ToInt32(intSize * col + ((intSize - intWidth) / (double)2) + 1), System.Convert.ToInt32(intSize * (row + 1))); if (ShowSolution ? Convert.ToBoolean(clsGrid .Solution[col][row]) : Convert.ToBoolean (clsGrid.Cells[col][row].intWest > 0)) e.Graphics.DrawLine(ShowSolution ? pGreen : pRed, System.Convert.ToInt32(intSize * col), System.Convert.ToInt32(intSize * row + ((intSize - intWidth) / (double)2) + 1), System.Convert.ToInt32(intSize * col + ((intSize - intWidth) / (double)2) + 1), System.Convert.ToInt32(intSize * row + ((intSize - intWidth) / (double)2) + 1)); if (ShowSolution ? Convert.ToBoolean(clsGrid .Solution[col][row]) : Convert.ToBoolean (clsGrid.Cells[col][row].intEast > 0)) e.Graphics.DrawLine(ShowSolution ? pGreen : pRed, System.Convert.ToInt32(intSize * col + ((intSize - intWidth) / (double)2) - 1), System.Convert.ToInt32(intSize * row + ((intSize - intWidth) / (double)2) + 1), System.Convert.ToInt32(intSize * (col + 1)), System.Convert.ToInt32(intSize * row + ((intSize - intWidth) / (double)2) + 1)); } } } protected override void WndProc(ref System.Windows.Forms.Message m) { if (m.Msg == WM_KEYDOWN) { if (ShowSolution) return; int row = base.CurrentCell.RowIndex; int col = base.CurrentCell.ColumnIndex; int msgVal = m.WParam.ToInt32(); switch ((Keys)msgVal) { case Keys.Up: if (row > 0) { if (clsGrid.Cells[col][row].blnNorth == false) { clsGrid.Cells[col][row].intNorth += 1; clsGrid.Cells[col][row - 1].intSouth += 1; base.CurrentCell = base.Rows[row - 1].Cells[col]; } } break; case Keys.Down: if (row < base.Rows.Count - 1) { if (clsGrid.Cells[col][row].blnSouth == false) { clsGrid.Cells[col][row].intSouth += 1; clsGrid.Cells[col][row + 1].intNorth += 1; base.CurrentCell = base.Rows[row + 1].Cells[col]; } } break; case Keys.Left: if (col > 0) { if (clsGrid.Cells[col][row].blnWest == false) { clsGrid.Cells[col][row].intWest += 1; clsGrid.Cells[col - 1][row].intEast += 1; base.CurrentCell = base.Rows[row].Cells[col - 1]; } } break; case Keys.Right: if (col < base.Columns.Count - 1) { if (clsGrid.Cells[col][row].blnEast == false) { clsGrid.Cells[col][row].intEast += 1; clsGrid.Cells[col + 1][row].intWest += 1; base.CurrentCell = base.Rows[row].Cells[col + 1]; } } break; } base.Refresh(); return; } else if (m.Msg == WM_LBUTTONDBLCLK || m.Msg == WM_LBUTTONDOWN) return; base.WndProc( ref m); } } }
VB.NET
Public Class Maze Inherits DataGridView Dim WM_LBUTTONDOWN As Integer = &H201 Dim WM_LBUTTONDBLCLK As Integer = &H203 Dim WM_KEYDOWN As Integer = &H100 Public Sub New() Me.DoubleBuffered = True End Sub Private blnSolution As Boolean Public Property ShowSolution() As Boolean Get Return blnSolution End Get Set(ByVal value As Boolean) blnSolution = value End Set End Property Protected Overrides Sub OnRowPrePaint(ByVal e As _ System.Windows.Forms.DataGridViewRowPrePaintEventArgs) e.PaintParts = e.PaintParts And Not _ DataGridViewPaintParts.Focus MyBase.OnRowPrePaint(e) End Sub Protected Overrides Sub OnPaint(ByVal e As _ System.Windows.Forms.PaintEventArgs) MyBase.OnPaint(e) If clsGrid.Cells Is Nothing Then Return For row As Integer = 0 To MyBase.Rows.Count - 1 For col As Integer = 0 To MyBase.Columns.Count - 1 Dim intWidth As Integer = 2 Dim pRed As New Pen(Color.Red, intWidth) Dim pGreen As New Pen(Color.Green, intWidth) Dim intSize As Integer = _ MyBase.Rows(0).Cells(0).Size.Width If If(ShowSolution(), clsGrid.Solution(col)(row), _ clsGrid.Cells(col)(row)).intNorth > 0 Then e.Graphics.DrawLine(If(ShowSolution(), pGreen, _ pRed), CInt(intSize * col + ((intSize - _ intWidth) / 2) + 1), CInt(intSize * row), _ CInt(intSize * col + ((intSize - intWidth) / 2) _ + 1), CInt(intSize * row + ((intSize - _ intWidth) / 2) + 1)) End If If If(ShowSolution(), clsGrid.Solution(col)(row), _ clsGrid.Cells(col)(row)).intSouth > 0 Then e.Graphics.DrawLine(If(ShowSolution(), pGreen, _ pRed), CInt(intSize * col + ((intSize - _ intWidth) / 2) + 1), CInt(intSize * row + _ ((intSize - intWidth) / 2) + 1), CInt(intSize * _ col + ((intSize - intWidth) / 2) + 1), _ CInt(intSize * (row + 1))) End If If If(ShowSolution(), clsGrid.Solution(col)(row), _ clsGrid.Cells(col)(row)).intWest > 0 Then e.Graphics.DrawLine(If(ShowSolution(), pGreen, _ pRed), CInt(intSize * col), CInt(intSize * row _ + ((intSize - intWidth) / 2) + 1), CInt(intSize _ * col + ((intSize - intWidth) / 2) + 1), _ CInt(intSize * row + ((intSize - intWidth) / 2) _ + 1)) End If If If(ShowSolution(), clsGrid.Solution(col)(row), _ clsGrid.Cells(col)(row)).intEast > 0 Then e.Graphics.DrawLine(If(ShowSolution(), pGreen, _ pRed), CInt(intSize * col + ((intSize - _ intWidth) / 2) - 1), CInt(intSize * row + _ ((intSize - intWidth) / 2) + 1), CInt(intSize * _ (col + 1)), CInt(intSize * row + ((intSize - _ intWidth) / 2) + 1)) End If Next Next End Sub Protected Overrides Sub WndProc(ByRef m As _ System.Windows.Forms.Message) If m.Msg = WM_KEYDOWN Then If ShowSolution Then Return Dim row As Integer = MyBase.CurrentCell.RowIndex Dim col As Integer = MyBase.CurrentCell.ColumnIndex If m.WParam.ToInt32 = Keys.Up Then If row > 0 Then If clsGrid.Cells(col)(row).blnNorth = False Then clsGrid.Cells(col)(row).intNorth += 1 clsGrid.Cells(col)(row - 1).intSouth += 1 MyBase.CurrentCell = MyBase.Rows(row - _ 1).Cells(col) End If End If ElseIf m.WParam.ToInt32 = Keys.Down Then If row < MyBase.Rows.Count - 1 Then If clsGrid.Cells(col)(row).blnSouth = False Then clsGrid.Cells(col)(row).intSouth += 1 clsGrid.Cells(col)(row + 1).intNorth += 1 MyBase.CurrentCell = _ MyBase.Rows(row + 1).Cells(col) End If End If ElseIf m.WParam.ToInt32 = Keys.Left Then If col > 0 Then If clsGrid.Cells(col)(row).blnWest = False Then clsGrid.Cells(col)(row).intWest += 1 clsGrid.Cells(col - 1)(row).intEast += 1 MyBase.CurrentCell = _ MyBase.Rows(row).Cells(col - 1) End If End If ElseIf m.WParam.ToInt32 = Keys.Right Then If col < MyBase.Columns.Count - 1 Then If clsGrid.Cells(col)(row).blnEast = False Then clsGrid.Cells(col)(row).intEast += 1 clsGrid.Cells(col + 1)(row).intWest += 1 MyBase.CurrentCell = _ MyBase.Rows(row).Cells(col + 1) End If End If End If MyBase.Refresh() Return ElseIf m.Msg = WM_LBUTTONDBLCLK OrElse m.Msg = _ WM_LBUTTONDOWN Then Return End If MyBase.WndProc(m) End Sub End Class
Let’s see what happens here.
You specify the DoubleBuffered property in the class constructor. This prevents flickering when redrawing pictures. You created the ShowSolution property, which will allow you to see the Solution when clicked or play the game when it is not clicked.
You then override the RowPrePaint event which occurs before the DataGridView’s rows are painted, to identify which parts have the focus. The Paint event of the DataGridView is overridden, and this is where the fun happens. We loop through each cell and draw random connecting lines to form a maze.
Lastly, you override WndProc, which is the normal input for a window to intercept the keys that are pressed. This allows us to navigate through the maze in any cardinal direction and keep track of it by drawing a line as we try to navigate through the maze.
Conclusion
We are almost done. We must now add this to a form, and this is what we will do in Part 3. Until then, happy coding!