Reputation: 397
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.
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
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