首先你需要从 TabControl 创建一个派生类。到目前为止还不错,但现在它变脏了。
因为 TabControl 不会调用 OnPaint,我们必须重写 WndProc 来处理 WM_PAINT 消息。在那里,我们继续用我们喜欢的颜色绘制我们的背景。
protected override void WndProc(ref Message m)
{
base.WndProc(ref m);
if(m.Msg == (int) WindowsMessages.Win32Messages.WM_PAINT)
{
using (Graphics g = this.CreateGraphics())
{
//Double buffering stuff...
BufferedGraphicsContext currentContext;
BufferedGraphics myBuffer;
currentContext = BufferedGraphicsManager.Current;
myBuffer = currentContext.Allocate(g,
this.ClientRectangle);
Rectangle r = ClientRectangle;
//Painting background
if(Enabled)
myBuffer.Graphics.FillRectangle(new SolidBrush(_backColor), r);
else
myBuffer.Graphics.FillRectangle(Brushes.LightGray, r);
//Painting border
r.Height = this.DisplayRectangle.Height +1; //Using display rectangle hight because it excludes the tab headers already
r.Y = this.DisplayRectangle.Y - 1; //Same for Y coordinate
r.Width -= 5;
r.X += 1;
if(Enabled)
myBuffer.Graphics.DrawRectangle(new Pen(Color.FromArgb(255, 133, 158, 191), 1), r);
else
myBuffer.Graphics.DrawRectangle(Pens.DarkGray, r);
myBuffer.Render();
myBuffer.Dispose();
//Actual painting of items after Background was painted
foreach (int index in ItemArgs.Keys)
{
CustomDrawItem(ItemArgs[index]);
}
}
}
}
我正在用这个方法做进一步的绘图,所以这个问题看起来有点矫枉过正,但忽略不必要的东西。
还要注意foreach 循环。稍后我会谈到这个。
问题是TabControl 在之前 绘制它自己的 WM_PAINT 的项目(标签标题),因此我们的背景将被绘制在顶部,这使得它们不可见。为了解决这个问题,我为DrawItem 制作了一个EventHandler,如下所示:
private void DrawItemHandler(object sender, DrawItemEventArgs e)
{
//Save information about item in dictionary but dont do actual drawing
if (!ItemArgs.ContainsKey(e.Index))
ItemArgs.Add(e.Index, e);
else
ItemArgs[e.Index] = e;
}
我将DrawItemEventArgs 保存到字典中(在我的例子中称为“ItemArgs”),以便以后可以访问它们。这就是几秒钟前的foreach 发挥作用的地方。它调用了一个方法,我正在绘制选项卡标题,该方法将我们之前保存的DrawItemEventArgs 作为参数来绘制正确状态和位置的项目。
所以,简而言之,我们正在拦截选项卡标题的绘制以延迟它,直到我们完成背景绘制。
此解决方案不是最佳解决方案,但它有效,并且您可以做的唯一事情是更好地控制TabControl(哈哈),而无需从头开始绘制。