You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C# WinForms数独开发:如何触发被覆盖主按钮的OnMouseHover事件?

解决WinForms数独游戏悬停小按钮时主按钮Hover失效的问题

这里提供三个实用的解决方案,你可以根据开发习惯选择:

方案一:让小按钮透传鼠标事件给主按钮

小按钮覆盖主按钮会拦截鼠标事件,我们可以让小按钮把鼠标消息转发给主按钮,确保主按钮的OnMouseHover能正常触发。

简单实现:直接调用主按钮的Hover方法

给每个小按钮绑定MouseEnter和MouseMove事件,主动触发主按钮的悬停逻辑:

// 假设当前主按钮实例为mainBtn,小按钮的MouseEnter事件处理
private void SmallButton_MouseEnter(object sender, EventArgs e)
{
    mainBtn.OnMouseHover(e);
}

// 若需要持续保持悬停状态,可在MouseMove里也调用
private void SmallButton_MouseMove(object sender, MouseEventArgs e)
{
    mainBtn.OnMouseHover(e);
}

自定义控件方式(一劳永逸)

写一个自定义按钮类,自动把鼠标消息转发给指定的主按钮:

public class PassThroughButton : Button
{
    // 要转发事件的目标控件(主按钮)
    public Control TargetControl { get; set; }

    protected override void WndProc(ref Message m)
    {
        base.WndProc(ref m);
        if (TargetControl == null) return;

        // 转发核心鼠标消息
        switch (m.Msg)
        {
            case 0x200: // WM_MOUSEMOVE
            case 0x20A: // WM_MOUSEHOVER
                Message forwardMsg = Message.Create(TargetControl.Handle, m.Msg, m.WParam, m.LParam);
                TargetControl.WndProc(ref forwardMsg);
                break;
        }
    }
}

使用时把小按钮替换成这个自定义控件,设置TargetControl为对应主按钮即可。

方案二:放弃小按钮,自定义绘制数字选项

直接在主按钮上绘制1-9的数字区域,完全避免子控件覆盖问题:

  1. 给主按钮添加状态标记和基础事件:
private bool _showNumOptions = false;

// 鼠标悬停时显示数字选项
private void MainButton_MouseHover(object sender, EventArgs e)
{
    _showNumOptions = true;
    this.Invalidate(); // 触发重绘
}

// 鼠标离开时隐藏
private void MainButton_MouseLeave(object sender, EventArgs e)
{
    _showNumOptions = false;
    this.Invalidate();
}
  1. 重写主按钮的OnPaint方法,绘制3x3数字网格:
protected override void OnPaint(PaintEventArgs e)
{
    base.OnPaint(e);
    if (!_showNumOptions) return;

    int cellSize = Math.Min(this.Width, this.Height) / 3;
    var g = e.Graphics;

    // 绘制9个数字单元格
    for (int i = 0; i < 9; i++)
    {
        int x = (i % 3) * cellSize;
        int y = (i / 3) * cellSize;

        // 绘制单元格背景和边框
        g.FillRectangle(Brushes.LightBlue, x, y, cellSize, cellSize);
        g.DrawRectangle(Pens.DarkBlue, x, y, cellSize, cellSize);

        // 居中绘制数字
        var numText = (i + 1).ToString();
        var textSize = g.MeasureString(numText, this.Font);
        g.DrawString(numText, this.Font, Brushes.Black, 
            x + (cellSize - textSize.Width) / 2, 
            y + (cellSize - textSize.Height) / 2);
    }
}
  1. 在主按钮的MouseClick事件里判断选中的数字:
private void MainButton_MouseClick(object sender, MouseEventArgs e)
{
    if (!_showNumOptions) return;

    int cellSize = Math.Min(this.Width, this.Height) / 3;
    int col = e.X / cellSize;
    int row = e.Y / cellSize;
    int selectedNum = row * 3 + col + 1;

    // 处理选中数字的逻辑,比如设置主按钮文本
    this.Text = selectedNum.ToString();
    _showNumOptions = false;
    this.Invalidate();
}

方案三:跟踪鼠标是否在主按钮/小按钮范围内

通过判断鼠标位置控制小按钮显示状态,避免误触发隐藏:

  1. 定义小按钮集合和状态标记:
private List<Button> _smallButtons = new List<Button>();
private bool _isHoveringGrid = false;
  1. 给主按钮和所有小按钮绑定MouseEnter事件:
private void GridControl_MouseEnter(object sender, EventArgs e)
{
    _isHoveringGrid = true;
    foreach (var btn in _smallButtons)
    {
        btn.Visible = true;
    }
}
  1. 给主按钮和所有小按钮绑定MouseLeave事件,检查鼠标位置:
private void GridControl_MouseLeave(object sender, EventArgs e)
{
    Point globalMousePos = Cursor.Position;

    // 检查鼠标是否在主按钮范围内
    bool inMainBtn = this.ClientRectangle.Contains(this.PointToClient(globalMousePos));
    // 检查鼠标是否在任何小按钮范围内
    bool inSmallBtn = _smallButtons.Any(btn => btn.ClientRectangle.Contains(btn.PointToClient(globalMousePos)));

    if (!inMainBtn && !inSmallBtn)
    {
        _isHoveringGrid = false;
        foreach (var btn in _smallButtons)
        {
            btn.Visible = false;
        }
    }
}

内容的提问来源于stack exchange,提问作者Fox_Nyssame

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.20 14:52:36