完全可配置的 MDI 标签控件(从头开始)






4.87/5 (111投票s)
一个完全可配置的选项卡控件,可用作带选项卡的 MDI 窗体。
引言
与其费力处理 `Forms.TabControl`,不如从头编写一个应该很有趣。我的目标是创建一个具有特定行为且易于使用的选项卡控件,同时具备资源管理器继承、图形、委托和集合。
此选项卡控件专门用于创建 MDI 界面,每个窗体一个选项卡页。实现的一些行为包括:
- 始终将新页面打开在第一个选项卡
- 隐藏无法容纳在屏幕上的选项卡页
- 不滚动选项卡页,而是选择隐藏的选项卡页时将其带到第一个位置
- 在下拉菜单中包含选项卡页(窗体)的列表
- 通过拖动重新排序选项卡的能力
- 易于拥有者绘制
- 选项卡上的关闭按钮
- 单击选项卡的重叠部分 不会 选择其他选项卡,并且
- 单击边框外部 不会 选择选项卡(通过 `Region` 属性实现)
- 尽可能暴露最多的功能和可配置性
- 非常易于使用
这不是什么
嗯,这不是一个坞泊面板。此外,还有一些未实现的功能,如左/右 `Alignment`、`RightToLeft`、在设计时添加选项卡页的支持(在这种情况下没有多大意义)、容器选项卡页等。
这不是一个非常快的控件,因为我暴露了很多功能/自定义项,但我已尽力做到最好,使其“OK”并且看起来很酷。
可以自定义什么
几乎所有东西。要理解该控件,让我们看一下对象和区域。
- 前两行是 `TopSeparator`
- 紧随其后的是 `TabTop`
- 左侧是选项卡区域
- 右侧是控件按钮区域
- 选项卡之间的空间是 `TabOffset`
- 整个底部是窗体
从左到右,选项卡由以下区域组成:
TabPadLeft
图标
文本
CloseButton (关闭按钮)
TabPadRight
大多数自定义是通过属性实现的。所有属性都在 `TabControl` 中,其中一些属性在 `TabPage` 中,因此您可以更改每个特定选项卡的一些外观。
选项卡中的图标和文本分别是窗体的 `Icon` 和 `Text` 属性。控件中的所有颜色都可以更改。选项卡中的关闭按钮可以用图像替换。`TopSeparator`、选项卡图标、选项卡关闭按钮和控件按钮可以显示或不显示。还有更多。查看控件以了解可能的功能。
可以通过 `GetTabRegion` 事件更改选项卡形状。选项卡背景可以通过 `TabPaintBackGround` 事件进行 OwnerDraw,边框可以通过 `TabPaintBorder` 事件处理。事件按提到的顺序发生。
使用控件
要使用该控件,您首先需要将其添加到窗体中。理想情况下,您应该将控件在其上停靠。
之后,您可以通过设计器更改所有属性。
在控件中插入 `TabPage`(窗体)
' Create the form
Dim MyForm As New AnyForm
' Insert the provided form in the TabControl1
' and save the TabPage in the Form.Tag property
MyForm.Tag = TabControl1.TabPages.Add(MyForm)
您不显示窗体,而是将其添加到控件中。很简单,不是吗?在一个现有程序中,您只需将控件添加到主窗体,然后在调用 `Show` 方法的地方,用 `Add` 方法替换。
' replace the actual call by the new one
' MyForm.Show
TabControl1.TabPages.Add(MyForm)
您可能不需要更改窗体中的任何属性。`Add` 方法会更改必要的属性。
`TabControl.TabPages.Add` 是一个返回创建的 `TabPage` 的函数。您可以将其保存到变量中,也可以忽略返回值。在上面的示例中,我将 `TabPage` 保存在 `Form.Tag` 属性中,如果您想快速访问 `TabPage`,这是一个好主意。
激活控件中的现有窗体
' You can use the TabPage
DirectCast(MyForm.Tag, MdiTabControl.TabPage).Select()
'or just use the form
TabControl1.TabPages(MyForm).Select()
您可以通过 `Index` 或 `Form` 访问选项卡页,并且可以通过 `TabPage` 或 `Form` 获取 `Index`。
更改选项卡形状
Private Sub TabControl2_GetTabRegion(ByVal sender As Object, ByVal e As _
MdiTabControl.TabControl.GetTabRegionEventArgs) _
Handles TabControl2.GetTabRegion
' you can create a new point array or just modify the existing one
e.Points(1) = New Point(e.TabHeight - 2, 2)
e.Points(2) = New Point(e.TabHeight + 2, 0)
End Sub
上面的示例将默认形状更改为第一个图片中的彩色形状。
OwnerDraw 选项卡背景
Private Sub TabControl1_TabPaintBackground(ByVal sender As Object, ByVal e As _
MdiTabControl.TabControl.TabPaintEventArgs) _
Handles TabControl1.TabPaintBackground
' Say to the control to don't paint the background
e.Handled = True
' here add your code to paint the background
' the TabPaintEventArgs has several properties to help
End Sub
OwnerDraw 选项卡边框
Private Sub TabControl1_TabPaintBorder(ByVal sender As Object, ByVal e As _
MdiTabControl.TabControl.TabPaintEventArgs) _
Handles TabControl1.TabPaintBorder
' Say to the control to don't paint the border
e.Handled = True
' here add your code to paint the background
' the TabPaintEventArgs has several properties to help
End Sub
您可以将 `Handled` 属性保留为 false,这样控件将绘制边框,但您可以在控件绘制背景之后、边框绘制之前进行一些额外的绘制,正如您在第一个图片中的“MY MDI Form #2”选项卡中看到的那样。
代码内部
`TabPage` 类继承自 `System.Windows.Forms.Control`。控件本身是选项卡,窗体是它的一个属性。它公开了 `Click` 事件,当选项卡被单击/选择时会触发该事件。没有 `CloseClick` 事件,因为它被窗体的 `Close` 事件捕获。
`TabControl` 类继承自 `System.Windows.Forms.UserControl`。该类有三个内部类。`GetTabRegionEventArgs` 继承自 `System.EventArgs`,`TabPaintEventArgs` 继承自 `PaintEventArgs`,`TabPageCollection` 继承自 `CollectionBase`。
此外,还有一个 `Friend` 类 `ControlButton` 继承自 `System.Windows.Forms.Control`,它被 `DropButton` 和全局 `CloseButton` 使用。
选项卡中的关闭按钮不是包含的控件。它是控件本身的一部分,由选项卡的鼠标事件控制。
更新
对原始版本进行了一些增强和更改。zip 文件中的 `LastChanges.txt` 文件说明了已更改的内容。
新的 `TabGlassGradient` 属性值得在此进行评论。我为 Firefox 选项卡和 IE 7 选项卡中的 Glass Look 提供的解决方案非常简单。我从两种颜色中获取渐变,将渐变分成 1/3,然后反转底部。我不知道其他人是怎么做的,但这样效果很好。这是代码。
' returns a new gradient brush with the glass look
Friend Function CreateGlassGradientBrush( _
ByVal Rectangle As Rectangle, ByVal Color1 As Color, _
ByVal Color2 As Color) As Drawing2D.LinearGradientBrush
' creates a new gradient brush
Dim b As New Drawing2D.LinearGradientBrush(Rectangle, _
Color1, Color2, Drawing2D.LinearGradientMode.Vertical)
' creates a new bitmap to to render the gradient
Dim x As New Bitmap(1, Rectangle.Height)
Dim g As Graphics = Graphics.FromImage(x)
' paint the gradient on the bitmap
g.FillRectangle(b, New Rectangle(0, 0, 1, Rectangle.Height))
' create a new color blend with 4 colors and copy the
' colors from the bitmat
Dim c As New Drawing2D.ColorBlend(4)
c.Colors(0) = x.GetPixel(0, 0)
c.Colors(1) = x.GetPixel(0, x.Height / 3)
' the trick is here the 3rd color is the last color in
' the bitmap
c.Colors(2) = x.GetPixel(0, x.Height - 1)
' and the last color is the one at 1/3
c.Colors(3) = x.GetPixel(0, x.Height / 3)
c.Positions(0) = 0
c.Positions(1) = 0.335
c.Positions(2) = 0.335
c.Positions(3) = 1
' assign the color blend to the brush
b.InterpolationColors = c
g.Dispose()
x.Dispose()
Return b
End Function
不要忘记在打开测试项目中的主窗体之前重新编译。
暂时就这样。我要去喝一杯健怡可乐了!
历史
- 2011 年 7 月 4 日:更新了下载文件。