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

WinForms 自定义容器控件

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.63/5 (19投票s)

2005年1月6日

2分钟阅读

viewsIcon

144333

downloadIcon

2942

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

Sample Image - CustomContainerControl.jpg

引言

为 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。 这样做的原因是减少了 ScrollableControlContainerControl 的开销。 可能有更好的方法来构建这个控件,但我发现这对于快速显示来说已经足够了。 如果你使用 UserControl 作为子控件并将 Dock 设置为 Top,它可以简化加载代码等等,但是调整控件大小需要很长时间并且绘制速度不够快。

一旦你掌握了在控件上绘制的方法,子控件就变得简单而强大。 熟悉使用 Graphic 类。 诸如 DrawstringFillRectangle 等方法。

创建一个类,该类 InheritsControl

添加一些 ImportsControl 以管理 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

示例代码包含了您开始和扩展所需的一切。

© . All rights reserved.