Jake
Jake

Reputation: 397

C# .net 4.0 tabControl1_DrawItem event firing constantly

I added a feature to one of my programs recently and noticed something very odd. The feature required me to add a new tab to the tab control I am using. This then pushed the tabs past what could be displayed on the form with default sizes requiring the arrows to navigate the tabs on the tab control.

enter image description here

The problem I discovered is that if these arrows on the tab control exist, the tabControl1_DrawItem event was firing every few milliseconds causing the program to become sluggish and use about 15% to 20% CPU when just sitting there doing nothing after the form load event.

Once I increased the form size so the arrows didn't exist, the event only fired when the form was moved or a different tab was selected.

I searched all day for this and debugged and found the answer or I guess you can say workaround, but not here. So I guess I have two questions. Why does the event fire constantly if the arrows exist, and is there anything possible to prevent it from doing this since I am not able to prevent users of the application from resizing the form and make this happen?

This is the code I have under the event.

private void tabControl1_DrawItem(object sender, DrawItemEventArgs e)
{
Platform.Log(LogLevel.Debug, "DrawItem event is being called.");
//Draw the tabs on the tab conrol
e.DrawBackground();
using (Brush br = new SolidBrush(TabColors[tabControl1.TabPages[e.Index]]))
{
    e.Graphics.FillRectangle(br, e.Bounds);
    SizeF sz = e.Graphics.MeasureString(tabControl1.TabPages[e.Index].Text, e.Font);
    e.Graphics.DrawString(tabControl1.TabPages[e.Index].Text, e.Font, DGVBrushForeColor, e.Bounds.Left + (e.Bounds.Width - sz.Width) / 2, e.Bounds.Top + (e.Bounds.Height - sz.Height) / 2 + 1);

    Rectangle rect = e.Bounds;
    rect.Offset(0, 1);
    rect.Inflate(0, -1);
    e.Graphics.DrawRectangle(DGVPenBackColor, rect);
    e.DrawFocusRectangle();
}

//To Fill the blank portion of the end of the tab control
SolidBrush fillbrush = new SolidBrush(BACK_COLOR);
//draw rectangle behind the tabs
Rectangle lasttabrect = tabControl1.GetTabRect(tabControl1.TabPages.Count - 1);
Rectangle background = new Rectangle();
background.Location = new Point(lasttabrect.Right, 0);

//pad the rectangle to cover the 1 pixel line between the top of the tabpage and the start of the tabs
background.Size = new Size(tabControl1.Right - background.Left, lasttabrect.Height + 1);
e.Graphics.FillRectangle(fillbrush, background);
}

Upvotes: 2

Views: 564

Answers (1)

Jake
Jake

Reputation: 397

The answer was found thanks to Hans Passant's help. I implemented this code found here on stackoverflow in my application.

    public Form1()
    {
        InitializeComponent();

        int style = NativeWinAPI.GetWindowLong(this.Handle, NativeWinAPI.GWL_EXSTYLE);
        style |= NativeWinAPI.WS_EX_COMPOSITED;
        NativeWinAPI.SetWindowLong(this.Handle, NativeWinAPI.GWL_EXSTYLE, style);
    }

    internal static class NativeWinAPI
    {
        internal static readonly int GWL_EXSTYLE = -20;
        internal static readonly int WS_EX_COMPOSITED = 0x02000000;

        [DllImport("user32")]
        internal static extern int GetWindowLong(IntPtr hWnd, int nIndex);

        [DllImport("user32")]
        internal static extern int SetWindowLong(IntPtr hWnd, int nIndex, int dwNewLong);
    }

This is what caused the problem with the draw event firing constantly when the tab navigation arrows where present. To solve the problem I set the Multiline property of the tab control to true. This prevents the navigation arrows from showing up and it is acceptable to have multiple rows of tabs for me.

I also want to note that without this code the problem does not happen, but the tabs flicker constantly and crazy without the "WS_EX_COMPOSITED hack" as Hans called it. With double buffering everything using this hack, it makes everything work well so it is staying in there for now in this application.

Upvotes: 1

Related Questions