65.9K
CodeProject 正在变化。 阅读更多。
Home

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

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.87/5 (111投票s)

2006年11月18日

CPOL

5分钟阅读

viewsIcon

2357419

downloadIcon

20861

一个完全可配置的选项卡控件,可用作带选项卡的 MDI 窗体。

Sample Image - MDITabControl.png

引言

与其费力处理 `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 日:更新了下载文件。
© . All rights reserved.