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

C# WinForm切换Tab页时DrawItem事件重复触发问题

Why TabControl.DrawItem Triggers on Tab Switch & How to Handle It

Let me break this down clearly—first, why the event fires every time you switch tabs, then what you can do about it.

Why It Happens

The DrawItem event in WinForms' TabControl is built to fire whenever any part of the control needs to be redrawn. When you switch tabs, two key changes trigger a redraw:

  • The previously active tab loses its "selected" state, so its header needs to be repainted to reflect the unselected style.
  • The new active tab needs to be repainted to show its selected state.

This is just how WinForms' GDI-based rendering works—it relies on redrawing regions of the control that have changed, and tab switching qualifies as a change that affects the visual state of multiple tab headers. Your current code runs every time because the control is asking you to re-draw those headers to update their appearance.

How to Handle It

First, a quick reality check: if your drawing logic is as lightweight as the code you shared (drawing a small icon and text), you probably don't need to fix this. The event firing is normal, and this level of drawing won’t cause performance issues.

But if you have heavier drawing logic (like complex graphics or loading resources every time), here are a couple of practical approaches to optimize:

1. Skip Redrawing When Nothing Has Changed

Add checks to only run your drawing code if the tab’s content (text, icon, etc.) or state (selected/unselected) has actually changed. For example:

// Store the last drawn state for each tab
private Dictionary<int, (string Text, bool IsSelected)> _tabLastState = new Dictionary<int, (string, bool)>();

private void systemRecordTabControl_DrawItem_1(object sender, DrawItemEventArgs e)
{
    var tabPage = systemRecordTabControl.TabPages[e.Index];
    var currentState = (tabPage.Text, systemRecordTabControl.SelectedIndex == e.Index);
    
    // Check if the state matches the last drawn state
    if (_tabLastState.TryGetValue(e.Index, out var lastState) && lastState == currentState)
    {
        return; // No change, skip drawing
    }
    
    // Update the stored state
    _tabLastState[e.Index] = currentState;
    
    // Your existing drawing logic
    e.Graphics.DrawImage(Properties.Resources.icon, e.Bounds.Right - 15, e.Bounds.Top + 3, 11, 11);
    e.Graphics.DrawString(tabPage.Text, new Font("Arial", 12, FontStyle.Underline), Brushes.Black, e.Bounds.Left + 12, e.Bounds.Top + 4);
    e.DrawFocusRectangle();
}

2. Cache Drawn Content

For tabs with static content, pre-render their header to a Bitmap once, then just draw the cached bitmap in the DrawItem event instead of re-creating the graphics every time:

private Dictionary<int, Bitmap> _tabHeaderCache = new Dictionary<int, Bitmap>();

// Call this method when tabs are added, removed, or their content changes
private void CacheTabHeaders()
{
    _tabHeaderCache.Clear();
    foreach (TabPage tab in systemRecordTabControl.TabPages)
    {
        int index = systemRecordTabControl.TabPages.IndexOf(tab);
        var bounds = systemRecordTabControl.GetTabRect(index);
        var bmp = new Bitmap(bounds.Width, bounds.Height);
        
        using (var g = Graphics.FromImage(bmp))
        {
            // Draw the header content once into the bitmap
            g.DrawImage(Properties.Resources.icon, bounds.Right - 15, bounds.Top + 3, 11, 11);
            g.DrawString(tab.Text, new Font("Arial", 12, FontStyle.Underline), Brushes.Black, bounds.Left + 12, bounds.Top + 4);
        }
        
        _tabHeaderCache[index] = bmp;
    }
}

private void systemRecordTabControl_DrawItem_1(object sender, DrawItemEventArgs e)
{
    if (_tabHeaderCache.TryGetValue(e.Index, out var cachedBmp))
    {
        e.Graphics.DrawImage(cachedBmp, e.Bounds.Location);
    }
    e.DrawFocusRectangle(); // Keep this for accessibility
}

Don’t forget to call CacheTabHeaders() when you modify tabs, and dispose of the cached bitmaps when the control is disposed to avoid memory leaks.

3. Accept It As Normal Behavior

Again, for simple drawing like your current code, this event firing is totally harmless. WinForms is designed to handle frequent redraws efficiently. If you notice flickering, you can enable double buffering by creating a custom TabControl subclass and setting DoubleBuffered = true.

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.22 07:55:59