WinForms 自定义容器控件






4.63/5 (19投票s)
2005年1月6日
2分钟阅读

144333

2942
一个用于 WinForms VB.NET 的 UserControl,它包含子控件,并且可以滚动。 此示例展示了如何绘制自己的控件并构建自定义容器来列出它们。

引言
为 WinForms 构建一个容器控件比我最初想象的要容易。我为什么要构建一个?因为我需要一个自定义解决方案来查看已创建但无法放置到任何其他第三方控件中的控件。
看来 Microsoft 已经删除了 VB 6 for Windows 中的 Data Repeater,所以现在我们必须自己制作一个。
制作容器
容器只是一个 UserControl。 一些特别之处在于设置 AutoScroll=True 并添加一些事件来处理哪个控件被选中以及计数。
    Public ReadOnly Property Count() As Integer
        Get
            Return Me.Controls.Count
        End Get
    End Property
 
    Public ReadOnly Property SelectedItem() As ChildItem
        Get
            Return Me._SelectedChild
        End Get
    End Property
容器控件通过传递强类型集合来加载。
    Public Property Items() As SimpleItemCollection
        Get
            Return _Items
        End Get
        Set(ByVal Value As SimpleItemCollection)
            _Items = Value
            If Value Is Nothing Then
                Me.ClearAll()
            Else
                Me.LoadAll()
            End If
        End Set
    End Property
传入集合后,您可以使用自定义控件中的 LoadAll 方法加载控件。
    Private Sub LoadAll()
        Dim n As Integer
        Dim bSuspend As Boolean
        Dim flipper As Boolean
        Dim counter As Integer
        _Loading = True
        Me.Controls.Clear()
        Me.SuspendLayout()
        RaiseEvent SelectedItemChanged(Nothing)
        _SelectedChild = Nothing
        _Top = 0
        For Each item As SimpleItem In Me._Items
            Dim itm As New ChildItem(item)
            itm.SuspendLayout()
            Me.Controls.Add(itm)
            With itm
                .Left = 0
                .Width = Me.Width
                .Top = _Top
            End With
            itm.ResumeLayout()
            _Top += itm.Height
            If Not bSuspend Then
                ' This loads just enough controls at first
                ' so it appears as if the control is doing something
                If _Top > Me.Height Then
                    Application.DoEvents()
                    Me.SuspendLayout()
                    bSuspend = True
                End If
            End If
            AddHandler itm.ItemSelected, AddressOf ChildSelected
            AddHandler itm.ItemDoubleClicked, AddressOf _
                   ItemDoubleClicked_Handler
            'Select the first item
            If Me.Count = 1 Then
                itm.SetSelected()
            End If
        Next
        Me.ResumeLayout()
        RaiseEvent CountChanged(Me.Count)
        _Loading = False
        Me.ResumeLayout()
        ' Hide the Horizontal scrollbar that shows 
        ' up sometimes when the child
        ' controls are not less than the size of the container.
        Me.AutoScrollHandler.VisibleAutoScrollHorizontal = False
    End Sub
制作子控件
子控件继承自 Control。 这样做的原因是减少了 ScrollableControl 和 ContainerControl 的开销。 可能有更好的方法来构建这个控件,但我发现这对于快速显示来说已经足够了。 如果你使用 UserControl 作为子控件并将 Dock 设置为 Top,它可以简化加载代码等等,但是调整控件大小需要很长时间并且绘制速度不够快。
一旦你掌握了在控件上绘制的方法,子控件就变得简单而强大。 熟悉使用 Graphic 类。 诸如 Drawstring 和 FillRectangle 等方法。
创建一个类,该类 Inherits 自 Control。

添加一些 Imports 到 Control 以管理 ComponentModel 以及绘图。
Imports System.Drawing.Drawing2D
Imports System.ComponentModel
您需要添加一些关键的样式设置,这些设置在 Web 上几乎没有文档记录。 这些设置管理控件的绘制和滚动。
    Public Sub New()
        MyBase.New()
        ' This call is required by the Component Designer.
        InitializeComponent()
        Me.BackColor = Color.White
        ' Set the value of the double-buffering style bits to true.
        ' This paints the control off screen so there's no flicker effect.
        Me.SetStyle(ControlStyles.DoubleBuffer _
          Or ControlStyles.UserPaint _
          Or ControlStyles.AllPaintingInWmPaint, _
          True)
        ' This enables mouse support such as the Mouse Wheel
        setstyle(ControlStyles.UserMouse, True)
        ' This will repaint the control whenever it is resized
        setstyle(ControlStyles.ResizeRedraw, True)
        Me.UpdateStyles()
    End Sub
当创建控件时(由 Custom 容器处理),只需传入 Contact(在本例中是保存控件数据的类对象)。
    Public Sub New(ByVal item As SimpleItem)
        Me.New()
        _Contact = item
        Me.Height = 40
    End Sub
    Public ReadOnly Property Contact() As SimpleItem
        Get
            Return _Contact
        End Get
    End Property
绘制子控件必须由代码来管理。 与 UserControl 类不同,此控件不会自行绘制。 你失去了简单性,但在加载数百个控件时获得了速度。 此外,用你的代码给朋友和邻居留下深刻印象也很酷! 是的,没错。
    Protected Overrides Sub OnPaint(ByVal pe As _
                       System.Windows.Forms.PaintEventArgs)
        MyBase.OnPaint(pe)
        Dim ft As Font
        ' Get the font of the Parent
        If Not Me.Parent Is Nothing Then
            ft = Me.Parent.Font
        Else
            ft = New Font("Tahoma", 8, GraphicsUnit.Point)
        End If
        ' Paint the First Name and Last Name
        pe.Graphics.DrawString(Contact.FirstName _
           & " " & Contact.LastName, ft, _
           New SolidBrush(Me.Parent.ForeColor), 28, 6)
    End Sub
现在这只是一个简单而粗糙的例子。 你可以根据需要详细地绘制控件。 这就是大公司(第三方)的做法。
管理选择哪个项目只是捕获 MouseDown 并引发一个事件,该事件告诉父容器(在本例中是 Custom Container),该项目已被选中以取消选择所有其他项目并将 Me 移入视图。
    Private Sub pnlMain_MouseDown(ByVal sender As System.Object, _
         ByVal e As System.Windows.Forms.MouseEventArgs) _
               Handles MyBase.MouseDown
        If e.Button = MouseButtons.Left AndAlso AllowSelect Then
            Me.SetSelected()
            ' Repaint the control
            Invalidate()
        End If
    End Sub
    Public Sub SetSelected()
        RaiseEvent ItemSelected(Me)
        Selected = True
    End Sub
    Public Property Selected() As Boolean
        Get
            Return _Selected
        End Get
        Set(ByVal Value As Boolean)
            _Selected = Value
            If Value Then
                BackColor = Color.Gainsboro
            Else
                BackColor = Color.White
            End If
            Invalidate()
        End Set
    End Property
示例代码包含了您开始和扩展所需的一切。
