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
示例代码包含了您开始和扩展所需的一切。