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;
}
public new TabDrawMode DrawMode
{
get
{
return TabDrawMode.OwnerDrawFixed;
}
set
{
// No you dont.
}
}
public MyTabControl()
{
base.DrawMode = TabDrawMode.OwnerDrawFixed;
}
private struct TabItemInfo
{
public Color BackColor;
public Rectangle Bounds;
public Font Font;
public Color ForeColor;
public int Index;
public DrawItemState State;
public TabItemInfo(DrawItemEventArgs e)
{
this.BackColor = e.BackColor;
this.ForeColor = e.ForeColor;
this.Bounds = e.Bounds;
this.Font = e.Font;
this.Index = e.Index;
this.State = e.State;
}
}
private Dictionary<int, TabItemInfo> _tabItemStateMap = new Dictionary<int, TabItemInfo>();
private const int WM_PAINT = 0x000F;
private const int WM_ERASEBKGND = 0x0014;
// Cache context to avoid repeatedly re-creating the object.
// WM_PAINT is called frequently so it's better to declare it as a member.
private BufferedGraphicsContext _bufferContext = BufferedGraphicsManager.Current;
protected override void WndProc(ref Message m)
{
switch (m.Msg)
{
case WM_PAINT:
{
// Let system do its thing first.
base.WndProc(ref m);
// Custom paint Tab items.
HandlePaint(ref m);
break;
}
case WM_ERASEBKGND:
{
if (DesignMode)
{
// Ignore to prevent flickering in DesignMode.
}
else
{
base.WndProc(ref m);
}
break;
}
default:
base.WndProc(ref m);
break;
}
}
private Color _backColor = Color.FromArgb(31, 31, 31);
[Browsable(true)]
[EditorBrowsable(EditorBrowsableState.Always)]
public new Color BackColor
{
get
{
return _backColor;
}
set
{
_backColor = value;
}
}
private void HandlePaint(ref Message m)
{
using (var g = Graphics.FromHwnd(m.HWnd))
{
SolidBrush backBrush = new SolidBrush(BackColor);
Rectangle r = ClientRectangle;
using (var buffer = _bufferContext.Allocate(g, r))
{
if (Enabled)
{
buffer.Graphics.FillRectangle(backBrush, r);
}
else
{
buffer.Graphics.FillRectangle(backBrush, r);
}
// Paint items
foreach (int index in _tabItemStateMap.Keys)
{
DrawTabItemInternal(buffer.Graphics, _tabItemStateMap[index]);
}
buffer.Render();
}
backBrush.Dispose();
}
}
private void DrawTabItemInternal(Graphics gr, TabItemInfo tabInfo)
{
/* Uncomment the two lines below to have each TabItem use the same height.
** The selected TabItem height will be slightly taller
** which makes unselected tabs float if you choose to
** have a different BackColor for the TabControl background
** and your TabItem background.
*/
// int fullHeight = _tabItemStateMap[this.SelectedIndex].Bounds.Height;
// tabInfo.Bounds.Height = fullHeight;
SolidBrush backBrush = new SolidBrush(BackColor);
// Paint selected.
// You might want to choose a different color for the
// background or the text.
if ((tabInfo.State & DrawItemState.Selected) == DrawItemState.Selected)
{
gr.FillRectangle(backBrush, tabInfo.Bounds);
gr.DrawString(this.TabPages[tabInfo.Index].Text, tabInfo.Font,
SystemBrushes.ControlText, tabInfo.Bounds);
}
// Paint unselected.
else
{
gr.FillRectangle(backBrush, tabInfo.Bounds);
gr.DrawString(this.TabPages[tabInfo.Index].Text, tabInfo.Font,
SystemBrushes.ControlText, tabInfo.Bounds);
}
backBrush.Dispose();
}
型
进一步改进
SolidBrush
与其重新创建SolidBrush对象,不如考虑将它们声明为类的成员。 范例:
private SolidBrush _backBrush;
private SolidBrush _tabBackBrush;
private SolidBrush _tabForeBrush;
private Color _tabBackColor = Color.FromArgb(31, 31, 31);
public Color TabBackColor
{
get
{
return _tabBackColor;
}
set
{
_tabBackColor = value;
_tabBackBrush?.Dispose();
_tabBackBrush = new SolidBrush(_tabBackColor);
}
}
private Color _tabForeColor = Color.FromArgb(241, 241, 241);
public Color TabForeColor
{
get
{
return _tabForeColor;
}
set
{
_tabForeColor = value;
_tabForeBrush?.Dispose();
_tabForeBrush = new SolidBrush(_tabForeColor);
}
}
private Color _backColor = Color.FromArgb(31, 31, 31);
[Browsable(true)]
[EditorBrowsable(EditorBrowsableState.Always)]
public new Color BackColor
{
get
{
return _backColor;
}
set
{
_backColor = value;
_backBrush?.Dispose();
_backBrush = new SolidBrush(_backColor);
}
}
protected override void Dispose(bool disposing)
{
_backBrush.Dispose();
_tabBackBrush.Dispose();
_tabForeBrush.Dispose();
base.Dispose(disposing);
}
// force the tab background to the current BackColor
private void tabpage_Paint(object sender, PaintEventArgs e)
{
SolidBrush fillBrush = new SolidBrush(BackColor);
e.Graphics.FillRectangle(fillBrush, e.ClipRectangle);
}
7条答案
按热度按时间rryofs0p1#
TabControl
对定制的支持非常差。我使用this custom tab control取得了很好的成功。如果你想像我一样改变外观,代码是非常有用的。c0vxltue2#
我只能想到将Appearance属性更改为Buttons
MSDN TabControl Appearance
yyyllmsg3#
首先,你需要从TabControl创建一个派生类。到目前为止一切都很好,但现在它变得肮脏。
因为TabControl不会调用
OnPaint
,所以我们必须重写WndProc
来处理WM_PAINT消息。在那里,我们继续用我们喜欢的颜色涂背景。字符串
我在这个方法中做了进一步的绘制,所以它看起来有点矫枉过正,但忽略不必要的东西。还要注意
foreach
循环。我以后再谈这个。问题是
TabControl
在它自己的WM_PAINT之前**绘制了它的项目(标签页标题),所以我们的背景将被绘制在顶部,这使得它们不可见。为了解决这个问题,我为DrawItem
做了一个EventHandler
,如下所示:型
我将
DrawItemEventArgs
保存到一个字典中(在我的例子中称为“ItemArgs”),以便以后可以访问它们。这就是几秒钟前的foreach
发挥作用的地方。它调用了一个方法,我正在绘制选项卡标题,该方法接受我们之前保存的DrawItemEventArgs
作为参数,以正确的状态和位置绘制项目。因此,简单地说,我们正在拦截标签标题的绘制,以延迟它,直到我们完成绘制背景。
这个解决方案不是最佳的,但它的工作和它的唯一的事情,你可以做更多的控制
TabControl
(lol)没有画它从头开始。w1e3prcc4#
添加到@janhildebrandt的答案,因为它缺少一些关键部分,实际上使它的工作。
属性
TabControl
的DrawMode
属性必须设置为TabDrawMode.OwnerDrawFixed
,否则DrawItem
事件处理程序将不会触发。只需像这样重写派生的TabControl类中的属性:
字符串
DrawItemEventArgs
我不知道代码是在哪个版本中编写的,但存储
TabItem
的Graphics
对象在2023年在.Net 4.5及以上版本下将无法工作,也不需要。相反,考虑使用如下结构:
型
DrawItem事件处理函数
当已经从Control本身派生时,不要分配事件处理程序。使用
OnDrawItem(DrawItemEventArgs)
方法:型
WndProc改进
您的
TabControl
将在设计模式下 Flink 。通过检查
WM_ERASEBKGND
消息,可以轻松避免这种情况。只需在DesignMode
期间忽略它即可:型
进一步改进
SolidBrush
与其重新创建
SolidBrush
对象,不如考虑将它们声明为类的成员。范例:
型
设置样式
使用
ControlStyles.OptimizedDoubleBuffer
可能会进一步减少 Flink (如果有的话)。型
特色
TabItem中的TextAlignment
在绘制TabItem的文本时传递一个
StringFormat
对象,以像使用Label
一样定位文本型
jucafojl5#
更简单(IMO):向TabPage(不是顶层TabControl,而是其中的TabPage)添加绘制处理程序,然后以所需的颜色绘制背景矩形。
1.在设计器中或“手动”向TabPage添加Paint事件处理程序:
字符串
1.在paint方法中,将页面矩形绘制为您想要的颜色(在我的示例中,我希望它遵循标准BackColor):
型
rpppsulh6#
将Panel拖放到选项卡控件的顶部(而不是内部),并在属性中设置颜色。根据需要调用Panelx.Hide()和Panelx.Show()。
kiayqfof7#
不幸的是,backcolor属性是在绘制控件时处理的。我的建议是做我已经做过的事情,创建一个用户控件来模仿选项卡控制器。
我使用了一个菜单条作为选项卡,并将第二个用户控件作为填充停靠到父用户控件。在第二个用户控件中,我可以添加所需的任何选项卡。
更难的是,你必须构建所有的功能,使它作为一个选项卡控件工作。