如何在不闪烁的情况下更改选项卡控件上的图像
How to change an image on tab control without flickering
我在 C# TabControl 的 header 上放了一张图片。我想在光标悬停在图像上时更改图像。
app image
现在我这样调用 Control.Refresh 方法:
TabControl tabControl1 = new TabControl();
[...]
private void tabControl1_MouseMove(object sender, MouseEventArgs e)
{
[...]
tabControl1.Refresh();
}
private void tabControl1_DrawItem(object sender, DrawItemEventArgs e)
{
e.Graphics.DrawImage(imagefile, arg1, arg2, arg3, arg4);
[...]
}
通过使用flag,在光标即将移到图像上并离开图像时调用Refresh方法,即不会连续多次调用Refresh方法。
这样,图像成功改变,但经常闪烁。
如何在不闪烁的情况下更改它?有什么想法吗?
为此您需要双缓冲区。
"Double Buffer is a technique where we draw all our graphic needs to an image stored in the memory (buffer) and after we are done with all our drawing needs we a draw a complete image from the memory onto the screen. This concentrates the drawing to the screen (an operation that badly effects the performance of the application) to a single operation rather than many small ones."
见https://www.codeproject.com/kb/graphics/doublebuffering.aspx
TabControl
比较特殊,不能开启双缓冲。这是因为它请求活动选项卡的所有子元素将自己绘制到其内容上。不幸的是,即使您不想自定义页面绘图而只想自定义选项卡,这也会影响选项卡。
但是,您可以采取一些技巧来最大程度地减少闪烁:
1.永远不要使用 Refresh()
Refresh()
方法强制整个控件立即重新绘制,无论您此时正在做什么。相反,使用 Invalidate
将控件标记为无效,因此它将与其他无效控件一起在专门的刷新会话中重新绘制。
2。使完整的表格双缓冲
虽然TabControl
不支持双缓冲,但是可以让整个Form
双缓冲,这样可以减少闪烁。
只需覆盖表单中的 CreateParams
即可:
private const int WS_EX_COMPOSITED = 0x02000000;
protected override CreateParams CreateParams
{
get
{
CreateParams cp = base.CreateParams;
cp.ExStyle |= WS_EX_COMPOSITED;
return cp;
}
}
3。仅使无效区域无效
如果您使整个 TabControl
无效,它将与其子项一起完全重新绘制。但是您想使选项卡中的某些 16x16 像素矩形无效,以便您可以节省整个控件的刷新。
private int hoveredIndex = -1;
private Size imageSize = new Size(16, 16);
private void TabControl1_MouseMove(object sender, MouseEventArgs e)
{
for (int i = 0; i < tabControl1.TabPages.Count; i++)
{
Rectangle rect = tabControl1.GetTabRect(i);
Rectangle closeButton = GetImageLocation(rect);
if (closeButton.Contains(e.Location))
{
if (hoveredIndex != i)
{
hoveredIndex = i;
tabControl1.Invalidate(rect);
}
}
else if (hoveredIndex == i)
{
tabControl1.Invalidate(tabControl1.GetTabRect(hoveredIndex));
hoveredIndex = -1;
}
}
}
private void TabControl1_MouseLeave(object sender, EventArgs e)
{
if (hoveredIndex != -1)
{
tabControl1.Invalidate(tabControl1.GetTabRect(hoveredIndex));
hoveredIndex = -1;
}
}
private Rectangle GetImageLocation(Rectangle rect)
{
return new Rectangle(rect.Right - imageSize.Width, rect.Top, imageSize.Width, imageSize.Height);
}
private void tabControl1_DrawItem(object sender, DrawItemEventArgs e)
{
Image image = e.Index == hoveredIndex ? imageHovered : imageNormal;
e.Graphics.DrawImage(image, GetImageLocation(e.Bounds));
}
我在 C# TabControl 的 header 上放了一张图片。我想在光标悬停在图像上时更改图像。
app image
现在我这样调用 Control.Refresh 方法:
TabControl tabControl1 = new TabControl();
[...]
private void tabControl1_MouseMove(object sender, MouseEventArgs e)
{
[...]
tabControl1.Refresh();
}
private void tabControl1_DrawItem(object sender, DrawItemEventArgs e)
{
e.Graphics.DrawImage(imagefile, arg1, arg2, arg3, arg4);
[...]
}
通过使用flag,在光标即将移到图像上并离开图像时调用Refresh方法,即不会连续多次调用Refresh方法。
这样,图像成功改变,但经常闪烁。
如何在不闪烁的情况下更改它?有什么想法吗?
为此您需要双缓冲区。
"Double Buffer is a technique where we draw all our graphic needs to an image stored in the memory (buffer) and after we are done with all our drawing needs we a draw a complete image from the memory onto the screen. This concentrates the drawing to the screen (an operation that badly effects the performance of the application) to a single operation rather than many small ones."
见https://www.codeproject.com/kb/graphics/doublebuffering.aspx
TabControl
比较特殊,不能开启双缓冲。这是因为它请求活动选项卡的所有子元素将自己绘制到其内容上。不幸的是,即使您不想自定义页面绘图而只想自定义选项卡,这也会影响选项卡。
但是,您可以采取一些技巧来最大程度地减少闪烁:
1.永远不要使用 Refresh()
Refresh()
方法强制整个控件立即重新绘制,无论您此时正在做什么。相反,使用 Invalidate
将控件标记为无效,因此它将与其他无效控件一起在专门的刷新会话中重新绘制。
2。使完整的表格双缓冲
虽然TabControl
不支持双缓冲,但是可以让整个Form
双缓冲,这样可以减少闪烁。
只需覆盖表单中的 CreateParams
即可:
private const int WS_EX_COMPOSITED = 0x02000000;
protected override CreateParams CreateParams
{
get
{
CreateParams cp = base.CreateParams;
cp.ExStyle |= WS_EX_COMPOSITED;
return cp;
}
}
3。仅使无效区域无效
如果您使整个 TabControl
无效,它将与其子项一起完全重新绘制。但是您想使选项卡中的某些 16x16 像素矩形无效,以便您可以节省整个控件的刷新。
private int hoveredIndex = -1;
private Size imageSize = new Size(16, 16);
private void TabControl1_MouseMove(object sender, MouseEventArgs e)
{
for (int i = 0; i < tabControl1.TabPages.Count; i++)
{
Rectangle rect = tabControl1.GetTabRect(i);
Rectangle closeButton = GetImageLocation(rect);
if (closeButton.Contains(e.Location))
{
if (hoveredIndex != i)
{
hoveredIndex = i;
tabControl1.Invalidate(rect);
}
}
else if (hoveredIndex == i)
{
tabControl1.Invalidate(tabControl1.GetTabRect(hoveredIndex));
hoveredIndex = -1;
}
}
}
private void TabControl1_MouseLeave(object sender, EventArgs e)
{
if (hoveredIndex != -1)
{
tabControl1.Invalidate(tabControl1.GetTabRect(hoveredIndex));
hoveredIndex = -1;
}
}
private Rectangle GetImageLocation(Rectangle rect)
{
return new Rectangle(rect.Right - imageSize.Width, rect.Top, imageSize.Width, imageSize.Height);
}
private void tabControl1_DrawItem(object sender, DrawItemEventArgs e)
{
Image image = e.Index == hoveredIndex ? imageHovered : imageNormal;
e.Graphics.DrawImage(image, GetImageLocation(e.Bounds));
}