Cursor.Hide() 函数的奇怪错误

Weird Bug With Cursor.Hide() function

我正在开发一个 Battleship 游戏项目,我制作了一个名为 GraphicCell 的控件,它继承自 PictureBox class,但用作面板的按钮。

与普通按钮的主要区别在于,当您将鼠标悬停在按钮上时,它会隐藏光标。它还有一个背景图像和一个名为 locked 的参数,我用它来锁定计算机的棋盘(玩家不能在这个棋盘上玩)。锁定的光标不隐藏

我的问题:当我将鼠标悬停在未锁定的 GraphicCell 上时,光标会按应有的方式隐藏,而对于 locked 的光标,它会按应有的方式显示光标,但是当我将鼠标悬停在 locked 上时,当我将鼠标悬停在未锁定的 GraphicCell.

上时,光标不会再次隐藏

GIF exemple for the bug: https://gyazo.com/750a2688a3d33d49462ff6b6e68533d1

Left (User's Board) - Not Locked

Right (PC's board) - Locked



代码:(错误应该在 MouseEnterEvent / MouseLeaveEvent 函数中)

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



编辑:问题已解决

感谢“OldBoyCoder”和“Hans Passant”解决了问题。

原来 Cursor.Show()/Cursor.Hide() 方法使用了一个计数器,所以问题是在 MouseLeaveEvent 中调用了 Cursor.Show() 方法,即使游标已经显示,所以 Cursor.Show() 方法有时被调用多次,它使计数器高于 1,这就是导致错误的原因。

(例如,如果 Cursor.Show() 的计数器为 2 并且我调用 Cursor.Hide(),则光标保持显示,因为 2 > 1)

修复:

已通过使用布尔值 locked 修复,以确保 Cursor.Show() 仅被调用一次。

更改:

private void MouseLeaveEvent(Object sender, EventArgs e)
{
    Cursor.Show();

    if (!locked)
    {
        if (this.Image != null)
            this.Image = null;
    }
}

收件人:

private void MouseLeaveEvent(Object sender, EventArgs e)
{
    if (!locked)
    {
        Cursor.Show();

        if (this.Image != null)
            this.Image = null;
    }
}

您从未在方法

中将锁定设置回 false
public void Update(Result r)

不是完整的答案,因为我还没有解决方案,但必须平衡对显示和隐藏的调用,因为有一个内部计数器:

发件人:

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

显示和隐藏方法调用必须平衡。每次调用 Hide 方法都必须对应调用 Show 方法。

您进行的所有 Show 调用都需要与 Hides 进行平衡。

您可以按任意顺序多次调用 vSetCursor(eCursorOperation.Show)vSetCursor(eCursorOperation.Hide)

enum eCursorOperation
{
    Show,
    Hide
}

// Since the cursor is showing to start with assume the internal counter is set to 1 - fingers crossed
private int GiCursorShowCount = 1; 

private void vSetCursor(eCursorOperation ecYourChoice)
{
    switch (ecYourChoice)
    {
        case eCursorOperation.Show:
            while (GiCursorShowCount < 1)
            {
                Cursor.Show();
                GiCursorShowCount++;
            }
            break;
        case eCursorOperation.Hide:
            while (GiCursorShowCount > 0)
            {
                Cursor.Hide();
                GiCursorShowCount--;
            }
            break;
        default:
            break;
    }
}