FireFox 式标签控件






4.61/5 (48投票s)
一篇关于 Tab Control 的文章
引言
最近我遇到了一个需求,需要从标签头本身关闭标签控件。这意味着用户不需要切换到任何选项卡页面就可以关闭它。一个完美的例子是 Firefox 浏览器。在 Firefox 中,用户可以打开任意数量的选项卡,并且可以随时关闭任何选项卡,而无需打开该选项卡。我尝试在 Google 上搜索解决方案,但没有找到完全符合我要求的内容。然后我想到了实现我自己的具有相同功能的选项卡控件。所以,这就是最终的控件。
Using the Code
该控件允许设计器像添加/删除普通选项卡控件一样添加/删除选项卡页面,并具有全新的外观。支持此功能的类是
- TabCtlEx.cs (继承自
System.Windows.Forms.TabControl
) - TabPage.cs (继承自
System.Windows.Forms.TabPage
)
使用此控件非常简单,就像 .NET 选项卡控件一样。
private MyControlLibrary.TabCtlEx userControl11;
TabPageEx tabPage1;
TabPageEx tabPage2;
TabPageEx tabPage3;
private TabPageEx tabPage4;
this.userControl11 = new MyControlLibrary.TabCtlEx();
this.tabPage1 = new MyControlLibrary.TabPageEx(this.components);
this.tabPage2 = new MyControlLibrary.TabPageEx(this.components);
this.tabPage3 = new MyControlLibrary.TabPageEx(this.components);
this.tabPage4 = new MyControlLibrary.TabPageEx(this.components);
this.Controls.Add(this. userControl11);
在每个选项卡头绘制关闭按钮
这需要您重写 .NET 选项卡控件中现有的 OnDrawItem()
函数。
/// <summary>
/// override to draw the close button
/// </summary>
/// <param name="e"></param>
protected override void OnDrawItem(DrawItemEventArgs e)
{
RectangleF tabTextArea = RectangleF.Empty;
for(int nIndex = 0 ; nIndex < this.TabCount ; nIndex++)
{
if( nIndex != this.SelectedIndex )
{
/*if not active draw ,inactive close button*/
tabTextArea = (RectangleF)this.GetTabRect(nIndex);
using(Bitmap bmp = new Bitmap(GetContentFromResource(
"closeinactive.bmp")))
{
e.Graphics.DrawImage(bmp,
tabTextArea.X+tabTextArea.Width -16, 5, 13, 13);
}
}
else
{
tabTextArea = (RectangleF)this.GetTabRect(nIndex);
LinearGradientBrush br = new LinearGradientBrush(tabTextArea,
SystemColors.ControlLightLight,SystemColors.Control,
LinearGradientMode.Vertical);
e.Graphics.FillRectangle(br,tabTextArea);
/*if active draw ,inactive close button*/
using(Bitmap bmp = new Bitmap(
GetContentFromResource("close.bmp")))
{
e.Graphics.DrawImage(bmp,
tabTextArea.X+tabTextArea.Width -16, 5, 13, 13);
}
br.Dispose();
}
string str = this.TabPages[nIndex].Text;
StringFormat stringFormat = new StringFormat();f
stringFormat.Alignment = StringAlignment.Center;
using(SolidBrush brush = new SolidBrush(
this.TabPages[nIndex].ForeColor))
{
/*Draw the tab header text
e.Graphics.DrawString(str,this.Font, brush,
tabTextArea,stringFormat);
}
}
}
在这里,关闭按钮实际上是绘制在每个选项卡头上的位图图像。因此,仅仅为了提供 Firefox 中使用的按钮的外观,使用了三个不同的位图图像
- closeinactive.bmp
- close.bmp
- onhover.bmp
这些图像嵌入到控件中,并使用反射提取。还有一个用于获取嵌入图像资源的函数。此函数返回流,并通过将流传递给位图类来创建图像,如上所述。
/// <summary>
/// Get the stream of the embedded bitmap image
/// </summary>
/// <param name="filename"></param>
/// <returns></returns>
private Stream GetContentFromResource(string filename)
{
Assembly asm = Assembly.GetExecutingAssembly();
Stream stream =asm.GetManifestResourceStream(
"MyControlLibrary."+filename);
return stream;
}
Firefox 会询问用户是否要关闭选项卡页面。我希望我的控件也具有相同的功能,因为有时用户可能会意外按下关闭按钮,并且不想丢失所做的更改。这可以通过设置 Boolean 属性来实现。
private bool confirmOnClose = true;
public bool ConfirmOnClose
{
get
{
return this.confirmOnClose;
}
set
{
this.confirmOnClose = value;
}
}
属性
ConfirmOnClose
在关闭选项卡页面之前,会通过消息框进行确认。以下是检查单击区域是否在位图矩形内部的代码。
protected override void OnMouseDown(MouseEventArgs e)
{
RectangleF tabTextArea = (RectangleF)this.GetTabRect(SelectedIndex);
tabTextArea =
new RectangleF(tabTextArea.X+tabTextArea.Width -16,5,13,13);
Point pt = new Point(e.X,e.Y);
if(tabTextArea.Contains(pt))
{
if(confirmOnClose)
{
if(MessageBox.Show("You are about to close "+
this.TabPages[SelectedIndex].Text.TrimEnd()+
" tab. Are you sure you want to continue?","Confirm close",
MessageBoxButtons.YesNo) == DialogResult.No)
return;
}
//Fire Event to Client
if(OnClose != null)
{
OnClose(this,new CloseEventArgs(SelectedIndex));
}
}
}
每当按下选项卡头上的关闭按钮时,控件将触发一个 OnClose()
事件给客户端,并将单击的选项卡索引作为参数。这将为客户端提供更大的灵活性,以便在关闭选项卡之前执行某些操作。
结论
这只是另一个提供一些灵活性和改进的用户界面的选项卡控件。我发现此功能在某些情况下非常有用。欢迎反馈!
历史
已更新菜单按钮功能。以上描述仅适用于添加关闭按钮,但文章源代码包含关闭按钮和菜单按钮的实际实现。此控件的主要优点是菜单项功能应用于每个选项卡页面,而不是选项卡控件。换句话说,我们可以为不同的选项卡页面设置不同的菜单项。
如果任何选项卡页面没有附加菜单,它将不会显示菜单按钮。下图显示 tabPage3 没有附加菜单,因此仅对该选项卡页面不显示菜单按钮。
附加菜单项的实际代码是
this.tabPage1.Menu
= contextMenuStrip1;
………
this.tabPage2.Menu
= contextMenuStrip1;
………
this.tabPage3.Menu
= contextMenuStrip1;
………
this.tabPage4.Menu
= contextMenuStrip1;
您可以为每个选项卡页面分配相同的菜单项或不同的菜单项。
历史
- 2007 年 8 月 16 日 -- 发布原始版本
- 2007 年 8 月 28 日 -- 文章和下载已更新
- 2008 年 6 月 24 日 -- 源代码已更新