I'm working on a Battleship game project and I made a control named GraphicCell
that inherits from the PictureBox
class but is functioning as a button for the board.
The main difference from a normal button is that it hides the cursor when you hover on it. It also has a background image and a parameter named locked
that I'm using to lock the computer's board (the player can't play on this board). The cursor is not hidden on the locked ones.
My Problem: When I hover over a non-locked GraphicCell
the cursor is hidden like it should, and for the locked
ones it shows the cursor like it should, but when I hover over a locked
one, the cursor doesn't hide again when I hover over a non-locked GraphicCell
.
GIF exemple for the bug: https://gyazo.com/750a2688a3d33d49462ff6b6e68533d1
Left (User's Board) - Not Locked
Right (PC's board) - Locked
Code: (Bug should be in the MouseEnterEvent
/ MouseLeaveEvent
functinons)
public class GraphicCell : PictureBox
{
enum SquareImage {None, Empty, Bombed};
readonly int x, y;
readonly Square s;
bool locked;
public GraphicCell(int x, int y, Square s, bool locked = false)
{
this.x = x;
this.y = y;
this.s = s;
this.locked = locked;
this.Size = new System.Drawing.Size(25, 25);
this.BackgroundImageLayout = ImageLayout.Stretch;
this.SizeMode = PictureBoxSizeMode.StretchImage;
this.BackgroundImage = Properties.Resources.water;
this.MouseEnter += new EventHandler(MouseEnterEvent);
this.MouseLeave += new EventHandler(MouseLeaveEvent);
}
public bool Locked { get { return locked; } set { locked = value; } }
public void Update(Result r)
{
if (r != Result.None)
{
locked = true;
switch (r)
{
case (Result.Miss):
ChangeImage(SquareImage.Empty);
break;
case (Result.Hit):
case (Result.ShipDestroyed):
case (Result.Victory):
ChangeImage(SquareImage.Bombed);
break;
}
}
}
private void ChangeImage(SquareImage si)
{
switch (si)
{
case (SquareImage.None):
this.Image = null;
break;
case (SquareImage.Empty):
this.Image = Properties.Resources.empty;
break;
case (SquareImage.Bombed):
this.Image = Properties.Resources.bombed;
break;
}
}
private void MouseEnterEvent(Object sender, EventArgs e)
{
if (!locked)
{
this.Image = Properties.Resources.water_bomb;
Cursor.Hide();
}
}
private void MouseLeaveEvent(Object sender, EventArgs e)
{
Cursor.Show();
if (!locked)
{
if (this.Image != null)
this.Image = null;
}
}
public Square GetSquare()
{ return this.s; }
public int GetX()
{ return this.x; }
public int GetY()
{ return this.y; }
}
Problem fixed thanks to "OldBoyCoder" & "Hans Passant".
Turns out that the Cursor.Show()
/Cursor.Hide()
methods use a counter, so the problem was that in the MouseLeaveEvent
the Cursor.Show()
method was called even if the cursor is already shown, so the Cursor.Show()
method was called sometimes more then once and it made the counter go higher then 1, which is what caused the bug.
(For exemple if the counter of Cursor.Show()
is 2 and I call Cursor.Hide()
the cursor remains shown since 2 > 1)
Fix:
Fixed by using the bool locked
to make sure Cursor.Show()
is being called only once.
Changed:
private void MouseLeaveEvent(Object sender, EventArgs e)
{
Cursor.Show();
if (!locked)
{
if (this.Image != null)
this.Image = null;
}
}
To:
private void MouseLeaveEvent(Object sender, EventArgs e)
{
if (!locked)
{
Cursor.Show();
if (this.Image != null)
this.Image = null;
}
}
Not a full answer as I don't have a solution yet but calls to Show and Hide must be balanced as there is an internal counter:
From:
The Show and Hide method calls must be balanced. For every call to the Hide method there must be a corresponding call to the Show method.
All those Show calls you're making need to be balanced out with Hides.