使用 MouseMove 确定绘制内容的闪烁选项卡控件

Flickering Tab Control with MouseMove Determining What To Draw

我一整天都在研究这个问题,(继续笑哈哈)但我没有看到任何解决闪烁控件的古老表单问题的方法。我的控件是 TabControl,我使用的是 DrawMode OwnerDrawFixed。我正在挂接以下事件。简而言之,我正在创建一个带有可关闭 "X" 按钮的 TabControl,这些按钮是 12x12 png 资源。关闭按钮都是灰色的,但如果我将鼠标悬停在一个按钮上,它应该使用不同的图像(红色 X)。

  1. MouseDown:循环所有 TabPage 并检查我是否单击了我正在绘制关闭按钮图像的矩形。

  2. MouseLeave:当我离开 TabControl 时我需要 Invalidate 以确保正确绘制所有内容

  3. MouseMove:循环所有 TabPage 并检查我是否将鼠标悬停在我正在绘制关闭按钮图像的矩形上。如果我将鼠标悬停在上方,那么我会保存标签页索引,以便我的绘图可以更改用于关闭按钮的图像。

  4. DrawItem:这里我简单画一下图

我测试过但运气不好...

  1. 制作我自己的 TabControl class 它继承了 TabControl 并且在构造函数中我将 OptimizedDoubleBuffering 的 SetStyles 设置为 true(我将其他建议的标志设置为 true)

  2. 我尝试覆盖 CreateParams 以便我可以或这个值... createParams.ExStyle |= 0x00000020; (我不知道这是做什么的,但看到一位用户建议这样做。

  3. 设置表单 DoubleBuffered(什么都不做)

无论如何,我不知道该怎么做,我已经读了一段时间了。

这是我所有活动的代码。我只想在我的选项卡上设置关闭按钮,当我将鼠标悬停在它们上方时这些按钮会突出显示。谢谢

    private int mousedOver = -1;//indicates which close button is moused over
    private void tabControl_DrawItem(object sender, DrawItemEventArgs e)
    {
        e.Graphics.DrawImage(e.Index == mousedOver ? Resources.redX : Resources.grayX, e.Bounds.Right - 15, e.Bounds.Top + 4);
    }
    private void tabControl_MouseDown(object sender, MouseEventArgs e)
    {
        TabControl tc = sender as TabControl;
        if (tc.TabCount == 1) return;

        for (int i = 0; i < tc.TabPages.Count; i++)
        {
            Rectangle r = tc.GetTabRect(i);
            Rectangle closeButton = new Rectangle(r.Right - 15, r.Top + 4, 12, 12);
            if (closeButton.Contains(e.Location))
            {
                TabPage tp = tc.TabPages[i];
                tc.TabPages.Remove(tp);
                tp.Dispose();
                break;
            }
        }
    }
    private void tabControl_MouseMove(object sender, MouseEventArgs e)
    {
        TabControl tc = sender as TabControl;
        for (int i = 0; i < tc.TabPages.Count; i++)
        {
            Rectangle r = tc.GetTabRect(i);
            Rectangle closeButton = new Rectangle(r.Right - 15, r.Top + 4, 12, 12);
            if (closeButton.Contains(e.Location))
            {
                mousedOver = i;
                tc.Invalidate();
                return;
            }
        }

        mousedOver = -1;
        tc.Invalidate();
    }
    private void tabControl_MouseLeave(object sender, EventArgs e)
    {
        TabControl tc = sender as TabControl;
        mousedOver = -1;
        tc.Invalidate();
    }

看来您的无效化太频繁了。尝试过滤它,以便仅在需要重新绘制控件时才使它无效:

private void tabControl_MouseMove(object sender, MouseEventArgs e) {
  TabControl tc = sender as TabControl;
  for (int i = 0; i < tc.TabPages.Count; i++) {
    Rectangle r = tc.GetTabRect(i);
    Rectangle closeButton = new Rectangle(r.Right - 15, r.Top + 4, 12, 12);
    if (closeButton.Contains(e.Location)) {
      if (mousedOver != i) {
        mousedOver = i;
        tc.Invalidate(r);
      }
    } else if (mousedOver == i) {
      int oldMouse = mousedOver;
      mousedOver = -1;
      tc.Invalidate(tc.GetTabRect(oldMouse));
    }
  }
}

我会保留 CreateParams 覆盖,但作为本机 windows 控件,您可能永远无法完全消除一些闪烁。

您也可以尝试设置控件的 DoubleBuffered 属性,方法是

Control.DoubleBuffered = true;

我知道这适用于 DataGridView、ListView、表单和面板。

可以在 MSDN 上找到文档。