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

MCCB - 多列组合框 - 如何创建简单的(用户控件)DLL

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.70/5 (14投票s)

2016年3月9日

CPOL

12分钟阅读

viewsIcon

24335

downloadIcon

1536

回想第一次,在我已经是一名 VB 和 VBA 的资深程序员时,我感到有必要构建我的第一个可重用 DLL。

首先

CodeProject 面向所有级别的程序员。本文档 **仅** 供从未构建过 DLL 的初学者和 VB 程序员阅读。有关更复杂的组合框,请参阅我的第一篇文章: ExCB - 扩展多列组合框

引言

下载的文件包含完整的项目,但我将展示创建此控件的基本步骤,该控件是一个能够永久显示一列或多列(例如“字段框”,带标签或不带标签)以及用于“记录”选择的列表的组合框。

DLL 通常是一个控件,由用户为特定目的创建的其他 System.Windows.Form 控件组成,通常灵活、可参数化,并准备好执行各种任务。

与其他控件一样,**UserControl** 具有自己的 **属性**、**方法** 和 **事件**,其中一些是我们自己创建的。

在开发 DLL 时,最好有一个另一个简单的 WinForms 项目来测试控件,这两个项目都在一个 **解决方案** 中,以便我们可以深入测试和调试控件的行为。

构建用户控件

  1. 打开 **Visual Studio**,启动 **新项目**,选择 **类库** 并将其命名为 **MCCB**,这将是项目名称、程序集名称和根命名空间 - 但 VS 会给你一个 VB **Public Class Class1**
  2. 菜单 **PROJECT**,选择 **Add User Control** 并将其命名为 **MCCB**,这将是您控件的未来名称 - VS 会创建另一个类并显示模板 - 一个 150x150 的正方形,更像一个 Panel 而不像一个 Form...

在此示例中,目标是创建一个组合框,该组合框公开一列或多列,并带有标签。我们创建第一列,所有其他列将在运行时创建。我们还需要一个下拉列表和一个用于打开列表的按钮。

  1. **调整** 控件的大小至约 200x200 并将其 **BackColor** 更改为其他(略微不同)的颜色。在运行时,此颜色将更改为 **Color.Control**,但在设计时我们需要一个视觉参考,在调整控件大小时。
  2. 让我们从标签(标题/标题栏)开始:从 **Toolbox** 中选择一个 **Label**。更改以下属性:Name="**Hdr0**",AutoSize=**False**,BackColor=**WhiteSmoke**,BorderStyle=**FixedSingle**,Location=**0;0**,Size=**100;19**,Text="**Hdr0**",TextAlign=**MiddleLeft**
  3. 现在是显示字段值的框。我们不使用 TextBox,而是使用 Label... 它是一个更轻量级的控件,而且值不打算被写入或更改。选择另一个 **Label** 并更改以下属性:Name="**Box0**",AutoSize=**False**,BackColor=**White**,BorderStyle=**FixedSingle**,ForeColor=**Navy**,Location=**0;18**,Size=**100;20**,Text="**Box0**",TextAlign=**MiddleLeft**
  4. 接下来是列表。选择一个 **ListView** 并更改属性:Name="**MyLV**",View=**Details**,BorderStyle=**FixedSingle**,FullRowSelect=**True**,GridLines=**True**,HeaderStyle=**None**,Location=**0;37**,MultiSelect=**False**,Size=**119;138**。这是一个有 8 行的列表,没有标题... 我们还将创建第一列:添加一个 **Column** 并更改属性:Name="**lvCol0**",Text=" ",Width=**97**。为什么是 97?为了完美地对齐行,ListView 列的宽度必须是相应标签/框宽度的减 1,第一个列除外,它必须是减 3...
  5. 最后是按钮,它必须显示任何种类的“向下箭头”。它可以是按钮、PictureBox,甚至是简单的 Label。我们用 Label 来做。选择一个 **Label** 并更改属性:Name="**LblDrop**",AutoSize=**False**,BackColor=**WhiteSmoke**,BorderStyle=**FixedSingle**,Location=**99;18**,Size=**20;20**,Text=" "。在属性 Image - Local resource - Import:**DArrow.png**,位于文件夹 **.\Image**

  1. 一切都 OK 并对齐了,对吗?但当控件放置在 Form 上时,列表必须隐藏... 因此,为了防止设计时垂直调整大小,并且因为标题+框的实际高度是 **38**,请在此用户控件的 **Resize Event** 中添加这个最开始的代码。
Private Sub MCCB_Resize(sender As Object, e As EventArgs) Handles Me.Resize
	Me.Height = 38
End Sub
  1. 现在是时候创建用于测试和调试控件的项目了。**保存** 项目并启动一个类型为 **Windows Forms Application** 的 **新项目**,将其命名为 **MCCB_Test** - VS 会给你一个 Form 模板(Form1)。**保存** 这个第二个项目。
  2. 添加用户控件项目:菜单 **FILE** - **Add** - **Existing Project** - 浏览 MCCB 项目并打开 *MCCB.vbproj* - 将创建一个名为 '**MCCB_Test**' 的解决方案,其中包含 Solution Explorer 中的两个项目。**选择** MCCB 项目,菜单 **BUILD** - **Build MCCB**。打开 Toolbox 并查看顶部... MCCB 控件已经在那儿了。**双击** 它 - 控件将被放置在 Form 上,名称为 **Mccb1**。**调整** 大小 - 如你所见,只允许水平调整大小。**启动** 解决方案 - **太棒了,控件在那儿!!!** 现在你可以管理这两个项目了。插入一段代码,菜单 **BUILD** - **Rebuild MCCB**,如果需要,在 Form1 中检查或更改控件属性,如果需要,设置断点进行调试,然后**启动** 解决方案。很简单。

我们即将创建一组 **属性**。通常,属性必须是 **Public** 并指定其数据类型。自定义属性的值可以像 IDE 属性窗口中的任何其他属性一样查看和更改,或在运行时更改。我们的大部分属性,除了一个,都可能在 IDE 属性窗口中读取和修改。其中一些会改变控件的外观:标题/框的 **高度**、**边框样式**、标题/框/列表的 **前景色** 和 **背景色**。这些属性意味着控件需要 重绘,我们稍后会看到。它们具有定义好的数据类型 - Integer、Byte、Color、BorderStyle - 可以使用预定义的对话框、组合框或文本框进行更改。但是,尽管属性 ListFontStyleLayerOrder 处理的是 Byte 值,但我们宁愿使用一个带有描述性文本的组合框来表示允许的值,而不是一个简单的文本框来输入数字。为此,我们必须声明我们 **Enum**eration 列表的数据类型。最后,属性 SelectedIndex 仅在运行时使用,即必须不可浏览且隐藏。在此 查看所有属性。

  1. 选择并打开 *MCCB.vb* 代码选项卡。让我们 **导入** 一个引用,这是指定一些属性属性所必需的,例如 Category、Description、Browsable 或 DesignerSerializationVisibility,并声明两个 **Enum** 类型和几个 **变量** - 一些用于属性值(变量的 _Name 是属性的 **Name**,前面加下划线),一些用于内部使用。
Imports System.ComponentModel   ' For PropertyAttributes

Public Class MCCB

   Public Enum LFStyle         ' for Property ListFontStyle
        SameAsControl = 0
        AlwaysRegular = 1
  End Enum
    Public Enum LayOrder        ' for Property LayerOrder
        Normal = 0
        BringToFront = 1
        SendToBack = 2
   End Enum
    ' Property variables
    Private _MaxItemsDisp As Integer = 8
    Private _HeaderHeight As Byte = 19
    Private _HeaderBackColor As Color = Color.WhiteSmoke
    Private _HeaderForeColor As Color = Color.Black
    Private _HeaderBorderStyle As BorderStyle = Windows.Forms.BorderStyle.FixedSingle
    Private _BoxHeight As Byte = 20
    Private _BoxBackColor As Color = Color.White
    Private _BoxForeColor As Color = Color.MediumBlue
    Private _BoxBorderStyle As BorderStyle = Windows.Forms.BorderStyle.FixedSingle
    Private _ListBackColor As Color = Color.White
    Private _ListForeColor As Color = Color.Black
    Private _ListBorderStyle As BorderStyle = Windows.Forms.BorderStyle.FixedSingle
    Private _ListFontStyle As Byte = 0      ' 0-SameAsControl, 1-AlwaysRegular
    Private _LayerOrder As Byte = 0         ' 0-Normal, 1-BringToFront, 2-SendToBack
    Private _SelectedIndex As Integer = -1
    ' Internal variables
    Private _ColumnCount As Integer = 0
    Private _ColumnIndex As Integer = 0
    Private _TotalWidth As Integer = 99
    Private _LeftLocation As Integer = 0
    Private _ItemsCount As Integer = 0
    Private _Me_MinHeight As Integer = _HeaderHeight + _BoxHeight - 1
    Private _ListHeight As Integer = 0
    Private _ShowList As Boolean = False    ' used in Resize event
    Private _FNIlastIdx As Integer = -1     ' Last ColumnIndex searched
    Private _FNIlastStr As String = ""      ' Last String searched
    Private _FNI_iFrom As Integer = -1
    Private _Error As String = "Error"
  1. 如前所述,大多数属性都意味着控件需要 **重绘**。重绘控件意味着 Resize 事件将被触发。每当列表下拉或隐藏时,即当“按钮”**LblDrop** 被点击时,Resize 事件也将被调用。所以,Resize 事件必须处理两种不同的情况:1)列表要下拉(如果显示的项目),具有正确的 [高度]。2)列表要隐藏。为了控制其行为,我们创建了一个变量 _ShowList,其初始值为 False,从而确保设计模式下的正确缩放。因此,让我们更改用户控件的 **Resize Event**。请注意,_Me_MinHeight = _HeaderHeight + _BoxHeight - 1,因此 _Me_MinHeight 始终代表控件的(最小)高度,不含列表,列表的高度是显示的行数,每行 **17** 像素 [16(行高)+ 1(分隔线)] 加上 **1**(最后一行)。
    Private Sub MCCB_Resize(sender As Object, e As EventArgs) Handles Me.Resize
        'Me.Height = 38     ' Replaced by the code below:
        If _ShowList And _ItemsCount > 0 Then
            _ListHeight = Math.Min(_ItemsCount, _MaxItemsDisp) * 17 + 1
        Else
            _ListHeight = 0
        End If
        Me.Height = _Me_MinHeight + _ListHeight
        MyLV.Height = _ListHeight
    End Sub
  1. Resize 事件(也)将从其他几个情况被调用,但变量 _ShowList 必须设置为所需值。因此,让我们创建一个唯一的 Sub,它设置变量的值并调用 Resize 事件,该事件将在所有这些情况下被调用。
    Private Sub ShowHideList(Show As Boolean)
        _ShowList = Show
        MCCB_Resize(Nothing, Nothing)
    End Sub
  1. 现在,让我们提供一个机制来下拉或隐藏列表,当标签 **LblDrop** 被点击时。更重要的是,当鼠标悬停在标签上时,它的 BackColor 变为 Orange,并且 Cursor 变为 Hand。因此,让我们使用 MouseHoverMouseLeaveClick 事件。
    Private Sub LblDrop_MouseHover(sender As Object, e As EventArgs) Handles LblDrop.MouseHover
        Me.Cursor = Cursors.Hand
        sender.BackColor = Color.Orange
    End Sub
    Private Sub LblDrop_MouseLeave(sender As Object, e As EventArgs) Handles LblDrop.MouseLeave
        Me.Cursor = Cursors.Default
        sender.BackColor = Color.WhiteSmoke
    End Sub
    Private Sub LblDrop_Click(sender As Object, e As EventArgs) Handles LblDrop.Click
        ShowHideList(Show:=(Me.Height <= _Me_MinHeight))
    End Sub
  1. 现在是创建我们第一个 **Property** 的时候了。默认情况下,属性是可读的(**Get** 部分)和可写的(**Set** 部分)。但是,它们也可以声明为 **ReadOnly**(仅 Get 部分)或 **WriteOnly**(仅 Set 部分)。此外,还可以指定一些属性。在这种情况下,Category(现有或新创建的,属性将被包含在其中)和 Description(出现在属性窗口底部的说明)。要读取属性,请 **Return** 相关变量的值。要写入属性,请将相关变量 **set** 为指定的值 **value**,如果值要立即生效,请编写相应的操作。在这种情况下,只需调用 Sub ShowHideList,并带有参数 True,以设置变量 _ShowList 并**下拉**列表。
<Category("_MCCB specifics")> _
<Description("Maximum Items Displyed by the List")> _
    Public Property MaxItemsDisp() As Byte
        Get
            ' Get - if the Property is readable (or ReadOnly)
            Return _MaxItemsDisp
        End Get
        Set(ByVal value As Byte)
            ' Set - if the Property is writable (or WriteOnly)
            _MaxItemsDisp = value
            ShowHideList(True) ' Action
        End Set
    End Property
  1. 目前,让我们创建我们的第一个 **Method**,它允许添加列并设置一些(固定)属性。为了从外部访问,Method 必须是 **Public**。第一次调用此 Method 时,不会创建新列,而是更改已存在的列。Method 将接受三个参数。
  1. **Width** - 必需。列的宽度(以像素为单位)。值为 **0** 将创建一个隐藏的列。
  2. **Text** - 必需。列的标题(Header)。
  3. **Align** - 可选。列的水平对齐方式。默认(如果不指定)为左对齐。HorizontalAlignment 值(0=Left,1=Right,2=Center)用于定义 ListView 列(或例如 TextBoxes)的 TextAlign 属性。但是,**Labels** 使用其他值(ContentAlignment),除了水平位置外,还反映了垂直位置(Top、Middle、Bottom)。我们的“Headers”和“Boxes”需要“Middle”对齐,因此 HorizontalAlignment 值(0、1 和 2)必须转换为“Middle”ContentAlignment 值(分别为 16、64 和 32)。
    Public Sub AddColumn(Width As Integer, Text As String, _
                         Optional Align As HorizontalAlignment = 0)
        Dim Lbl_Align As ContentAlignment = If(Align = 1, 64, _
                                               If(Align = 2, 32, 16))
        Dim Hdr As Label
        Dim Box As Label
        Me.SuspendLayout()
        _ColumnCount += 1
        _ColumnIndex = _ColumnCount - 1
        If _ColumnCount = 1 Then   ' First Column, already exists
            Hdr = Hdr0                          ' Existing Header
            Box = Box0                          ' Existing Box
            MyLV.Width = Width + 19             ' Existing List. Initial
            ' value, including the "Button" (LblDrop)
            lvCol0.Width = Width - 3            ' Existing ListView Column
            _TotalWidth = Width - 1             ' Initial value
            Me.BackColor = Color.Transparent
        Else                        ' Column is to be created
            Hdr = New Label                         ' New Header
            Hdr.Name = "Hdr" & _ColumnIndex.ToString
            Hdr.Left = _TotalWidth
            Box = New Label                         ' New Box
            Box.Name = "Box" & _ColumnIndex.ToString
            Box.Left = _TotalWidth
            MyLV.Columns.Add(New ColumnHeader)      ' New ListView Column
            MyLV.Columns(_ColumnIndex).Name = "lvCol" & _ColumnIndex.ToString
            MyLV.Columns(_ColumnIndex).Text = ""
            MyLV.Columns(_ColumnIndex).Width = Math.Max(Width - 1, 0)
            MyLV.Columns(_ColumnIndex).TextAlign = Align
            MyLV.Width += Width - 1     ' Accum.
            _TotalWidth += Width - 1    ' Accum.
        End If
        Hdr.Width = Width           ' Header
        Hdr.Text = Text
        Hdr.TextAlign = Lbl_Align
        Box.Width = Width           ' Box
        Box.Text = ""
        Box.TextAlign = Lbl_Align
        LblDrop.Left = _TotalWidth  ' DropDown "Button"
        LblDrop.Height = _BoxHeight
        If _ColumnCount > 1 Then    ' All but first
            Controls.AddRange({Box, Hdr})
        End If
        Me.ResumeLayout(True)
        RedrawControls()
    End Sub

调用此 Method 的示例

Mccb1.AddColumn(140, "Name")    ' Align not specified = HorizontalAlignment.Left
Mccb1.AddColumn(35, "Age", HorizontalAlignment.Center)
Mccb1.AddColumn(90, "Due date", 2)  ' 2 = HorizontalAlignment.Center
Mccb1.AddColumn(90, "Amount (US$)", HorizontalAlignment.Right)
  1. 如前所述,AddColumn Method 创建新列,设置其不可更改的属性。但为了正确完成组件设置,我们必须初始化可以随时更改的属性。因此,我们将创建一个特定的(**Private**)**Sub**routine(RedrawControls),它也由 AddColumn Method 调用(最后一条指令):请注意,由于控件的大小已更改(Me.Height & Me.Width),控件的 Resize Event 将被触发。
    Private Sub RedrawControls()
        Me.SuspendLayout()
        MyLV.Top = _HeaderHeight + _BoxHeight - 2
        MyLV.BackColor = _ListBackColor
        MyLV.ForeColor = _ListForeColor
        MyLV.BorderStyle = _ListBorderStyle
        Dim Name As String
        For iCtr As Integer = 0 To _ColumnIndex ' For each Column...
            Name = "Box" & iCtr.ToString
            Controls(Name).Top = _HeaderHeight - 1
            Controls(Name).Height = _BoxHeight
            Controls(Name).BackColor = _BoxBackColor
            Controls(Name).ForeColor = _BoxForeColor
            Dim Lbl As Label = CType(Controls(Name), Label)
            Lbl.BorderStyle = _BoxBorderStyle
            Name = "Hdr" & iCtr.ToString
            Controls(Name).Height = _HeaderHeight
            Controls(Name).BackColor = _HeaderBackColor
            Controls(Name).ForeColor = _HeaderForeColor
            Lbl = CType(Controls(Name), Label)
            Lbl.BorderStyle = _HeaderBorderStyle
        Next
        LblDrop.Top = _HeaderHeight - 1
        LblDrop.Height = _BoxHeight
        _Me_MinHeight = _HeaderHeight + _BoxHeight - 1
        Me.Height = _Me_MinHeight
        Me.Width = _TotalWidth + 20 ' 20=LblDrop.Width
        Me.ResumeLayout(True)
    End Sub
  1. 查看提供的源项目。复制或复制所有其他调用 RedrawControls 的属性。特别注意两个操作不同的属性:第一个是 ListFontStyle,其类型由 Enum LFStyle 定义,它设置变量 _ListFontStyle 并调用 Event(FontChanged),该 Event 在字体更改时发生。第二个是 LayerOrder,其类型由 Enum LayOrder 定义,它设置变量 _LayerOrder,但其操作会提前进行,取决于其先前的值。为什么?因为,如果 a 和 b 没有疑问,将值设置为 0(Normal)意味着改变最后一个行为。该操作也必须在控件加载时在控件的 **Load Event** 中执行。让我们看看 LoadFontChanged 事件,以及这两个属性。
    Private Sub MCCB_Load(sender As Object, e As EventArgs) Handles MyBase.Load
        If _LayerOrder = 1 Then
            Me.BringToFront()
        ElseIf _LayerOrder = 2 Then
            Me.SendToBack()
        End If
    End Sub
    Private Sub MCCB_FontChanged(sender As Object, e As EventArgs) Handles Me.FontChanged
        MyLV.Font = New Font(Me.Font, If(_ListFontStyle = 0, _
                                        Me.Font.Style, FontStyle.Regular))
    End Sub
    <Category("_MCCB specifics")> _
    <Description("Action in List when Control's Font changes to Bold")> _
    Public Property ListFontStyle() As LFStyle
        Get
            Return _ListFontStyle
        End Get
        Set(ByVal value As LFStyle)
            _ListFontStyle = value
            MCCB_FontChanged(Nothing, Nothing)
        End Set
    End Property
    <Category("_MCCB specifics")> _
    <Description("Control's Layer Order Action")> _
    Public Property LayerOrder() As LayOrder
        Get
            Return _LayerOrder
        End Get
        Set(ByVal value As LayOrder)
            If value = 1 OrElse (value = 0 And _LayerOrder = 2) Then
                Me.BringToFront()
            ElseIf value = 2 OrElse (value = 0 And _LayerOrder = 1) Then
                Me.SendToBack()
            End If
            _LayerOrder = value
        End Set
    End Property
  1. 让我们创建一个 Method 来填充列表。它需要一个 **字符串数组**,每个 SubItem 一个,包括任何隐藏的列。很简单,因为这样的数组可以直接转换为所需的 ListViewItem 类型。它返回一个 Boolean,指示操作的成功(True)或失败(False)。
    Public Function AddRow(SubItems As Array) As Boolean
        Dim OK As Boolean = False
        If SubItems.Length > _ColumnIndex + 1 Then
            MsgBox("SubItems (" & SubItems.Length.ToString & _
                   ") exceeds the number of Columns (" & _
                   (_ColumnIndex + 1).ToString & ")...", _
                   MsgBoxStyle.Critical, _Error)
        Else
            Try
                MyLV.Items.Add(New ListViewItem(SubItems))
                _ItemsCount += 1
                OK = True
            Catch ex As Exception
                MsgBox(ex.Message, MsgBoxStyle.Critical, _Error)
            End Try
        End If
        Return OK
    End Function

调用此 Method 的示例

Dim vAge As Byte = 35
Dim vDate As String = Today.ToShortDateString
Dim vValue As Single = 1234.56
If Mccb1.AddRow({"John Smith", vAge.ToString, vDate, Format(vValue, "#,###.00")}) = False Then
   ' Something went wrong...
End If
  1. 当选择列表行时,该行的 **SubItem(s)** 必须显示在相应的 **Box(es)** 中。为了执行此任务,让我们创建一个 Sub ShowSelectedItem,以及执行任务的三种情况:1-我们控件的属性(不可浏览且隐藏)SelectedIndex,2-我们控件的 Method FindNextItem,以及 3-ListView 的 Event ItemSelectionChanged,它触发我们的控件的 Event(ItemSelectionChanged)(代码片段 #1),当选择新行时必须引发该 Event,并提供一个包含所选行所有 **SubItems** 值的**字符串数组**,还有一个简单的 Sub()(代码片段 #2)用于在检测到无效的负索引时显示错误消息,以及一个 Function(GetSubItems),它准备上述**字符串数组**,也用于 Method FindNextItem
    Public Event ItemSelectionChanged(SubItems As Array)
    Private Sub MsgNoNeg(Prefx As String)
        MsgBox(Prefx & " cannot be negative...", _
               MsgBoxStyle.Critical, _Error)
    End Sub
    Private Sub ShowSelectedItem()
        Dim BoxName As String
        For iCtr As Integer = 0 To _ColumnIndex
            BoxName = "Box" & iCtr.ToString
            Controls(BoxName).Text = MyLV.Items(_SelectedIndex).SubItems(iCtr).Text
        Next
    End Sub
    Private Function GetSubItems() As Array
        Dim SubItems(_ColumnIndex) As String
        For iSI As Integer = 0 To _ColumnIndex
            SubItems(iSI) = MyLV.Items(_SelectedIndex).SubItems(iSI).Text
        Next
        Return SubItems
    End Function
     <Browsable(False)> _
    <DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)> _
    Public Property SelectedIndex() As Integer
        Get
            Return _SelectedIndex
        End Get
        Set(ByVal value As Integer)
            If value < 0 Then
                MsgNoNeg("Index")
            Else
                If value >= _ItemsCount Then
                    MsgBox("Last Index = " & (_ItemsCount - 1).ToString, _
                           MsgBoxStyle.Critical, _Error)
                Else
                    If _SelectedIndex >= 0 Then
                        MyLV.Items(_SelectedIndex).Selected = False
                    End If
                    _SelectedIndex = value
                    MyLV.Items(_SelectedIndex).Selected = True
                    ShowSelectedItem()
                    ShowHideList(Show:=False)    'Close List
                End If
            End If
        End Set
    End Property
    Public Function FindNextItem(ColumnIndex As Integer, SearchString As String) As Array
        Dim FoundItem(_ColumnIndex) As String
        If ColumnIndex >= 0 Then
            If ColumnIndex > _ColumnIndex Then
                MsgBox("Maximum ColumnIndex is " & _ColumnIndex.ToString & "...", _
                       MsgBoxStyle.Critical, _Error)
            Else
                If ColumnIndex = _FNIlastIdx And SearchString = _FNIlastStr Then
                    ' Same values, serch next
                    _FNI_iFrom += 1
                Else
                    ' New values, search first
                    _FNI_iFrom = 0
                    _FNIlastIdx = ColumnIndex
                    _FNIlastStr = SearchString
                End If
                If _FNI_iFrom >= 0 Then

                    Dim Found As Boolean = False
                    Dim iCtr As Integer
                    For iCtr = _FNI_iFrom To _ItemsCount - 1
                        If Strings.InStr(MyLV.Items(iCtr).SubItems(ColumnIndex).Text, _
                                         SearchString, CompareMethod.Text) > 0 Then
                            ' Item found...
                            Found = True
                            _SelectedIndex = iCtr
                            FoundItem = GetSubItems()
                            ' Select Item
                            If _SelectedIndex >= 0 Then
                                MyLV.Items(_SelectedIndex).Selected = False
                            End If
                            MyLV.Items(_SelectedIndex).Selected = True
                            ShowSelectedItem()
                            ShowHideList(True)
                            Exit For
                        End If
                    Next
                    _FNI_iFrom = iCtr
                    If Found = False And iCtr = _ItemsCount Then
                        MsgBox("No match...", MsgBoxStyle.Information, "Info")
                        FoundItem = Nothing
                    End If
                End If
            End If
        Else
            MsgNoNeg("ColumnIndex")
        End If
        Return FoundItem
    End Function
    Private Sub MyLV_ItemSelectionChanged(sender As Object, e As ListViewItemSelectionChangedEventArgs) _
                Handles MyLV.ItemSelectionChanged
        Dim FoundItem(_ColumnIndex) As String
        If e.IsSelected Then
            _SelectedIndex = e.ItemIndex
            ShowSelectedItem()
            ShowHideList(False)     ' Close List
            RaiseEvent ItemSelectionChanged(GetSubItems)
        End If
    End Sub
  1. 再有两个简单的 Method,ClearDropDown
    Public Sub Clear()
        MyLV.Items.Clear()
        _ItemsCount = 0
        For iCtr As Integer = 0 To _ColumnIndex
            Controls("Box" & iCtr.ToString).Text = ""
        Next
        ShowHideList(False)     ' Close the List
    End Sub
    Public Sub DropDown()
        ShowHideList(True)      ' Drop-down the List
    End Sub

就是这样。我将控件的代码组织在 Regions 中。

Imports
Enums & Variables
Public Properties
Public Methods
Public Event
Control Events
Private Subs/Functions
Initialization

#Region "Initialization" 是 Designer 生成的代码。

最后,在你的 WinForms 应用程序中。

  1. 将 *MCCB.dll* 复制到其 **StartupPath**(项目内的 *\\bin\\Debug* 或 *\\bin\\Release*),然后复制到最终 .EXE 的文件夹。
  2. 在 Visual Studio 中,添加引用:菜单 *PROJECT - Add Reference... - Browse... - MCCB.dll*
  3. 在 Toolbox 中放置一个图标():菜单 **TOOLS - Choose Toolbox Items... - .NET Framework Components - Browse... - MCCB.dll**。图标将出现在 **All Windows Forms** 组下。

控件自身属性、方法和事件的简要说明。

属性

  • BoxBackColor - Box(es) 的背景色。
  • BoxBorderStyle - Box(es) 的边框样式。
  • BoxForeColor - Box(es) 的前景色。
  • BoxHeight - Box(es) 的高度。
  • HeaderBackColor - Header(s) 的背景色。
  • HeaderBorderStyle - Header(s) 的边框样式。
  • HeaderForeColor - Header(s) 的前景色。
  • HeaderHeight - Header(s) 的高度。
  • LayerOrder - 控件的图层顺序操作。
  • ListBackColor - 列表的背景色。
  • ListBorderStyle - 列表的边框样式。
  • ListFontStyle - 当控件字体更改为粗体时的列表操作。
  • ListForeColor - 列表的前景色。
  • MaxItemsDisp - 列表显示的最大项数。
  • SelectedIndex - 列表中被(或将要)选中的项的索引。

方法

  • AddColumn(Width As Integer, Text As String, [Align As HorizontalAlignment = HorizontalAlignment.Left]) - 添加一列,给定其宽度(像素)、其(标题)文本和其对齐方式(左、中、右)。
  • AddRow(SubItems As Array) As Boolean - 向列表添加一行,给定一个字符串数组。返回一个 Boolean,指示操作的成功(True)或失败(False)。
  • Clear - 清空控件的列表和框。
  • DropDown - 下拉列表。
  • FindNextItem(ColumnIndex As Integer, SearchString As String) As Array - 在给定 **Index** 的列值中查找给定 **String** 的下一个出现。匹配发生的行将被选中,并返回其 SubItems 的数组。当没有更多匹配项时,数组为 Nothing,但最后选中的行仍保持选中状态。

事件

  • ItemSelectionChanged(SubItems As Array) - 当从列表中选择新行时触发。

历史

2016 年 3 月 9 日 - 首次发布

© . All rights reserved.