Search code examples
c#cursorpicturebox

Weird Bug With Cursor.Hide() function


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



Edit: Problem Solved

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

Solution

  • 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:

    https://msdn.microsoft.com/query/dev11.query?appId=Dev11IDEF1&l=EN-US&k=k(System.Windows.Forms.Cursor.Hide);k(Hide);k(TargetFrameworkMoniker-.NETFramework,Version%3Dv4.5);k(DevLang-csharp)&rd=true

    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.