自定义列表框控件:第1部分收集事件

介绍 在这个小系列文 章中,我将解释如何编写一个带有扩展特性的自定义列表框控件,我相信大多数开发人员会发现这些扩展特性非常有用。除了要解释的特定特性外,这些文章还将介绍如何制作自己的自定义组件,为您提供自己添加到WinForms控件工具箱的组件。在第一篇文章中,我将讨论集合事件问题,更具体地说,是如何在添加或删除项时添加事件通知。 许多保存项目集合的。net WinForms控件在集合更改时都缺少重要事件。通常,您必须使用数据绑定或自定义数据源来解决这个问题,但有时,最好将事件连接到控件本身。因此,我们将讨论这些内容以及派生自定义组件的基础知识。守则的注释;我决定用VB。NET(尽管我更喜欢c#),因为我在VB上看到了更多关于这个主题的问题。网络论坛。 创建派生组件 经典的ListBox控件有它自己的实现,我们真的不能改变它太多。但是,我们可以创建自己的控件并继承经典ListBox控件的所有功能。我们可以通过创建用户控件并从头开始实现所有的特性来走一段很长的路,但那将花费无数个小时的开发,而这并不是这里真正的重点。我们想要的是保持基本的功能,只改变某些选定特性的行为。如果您熟悉继承的概念,那么理解它就没有问题。然而,您可能没有想到的是,类继承对控件的工作方式是相同的。毕竟,像Label这样的控件是一个类,就像您要在其中编写代码的其他类一样。 让我们用一个小例子来试试: 创建一个新的Windows应用程序。向项目添加一个新类,并将其命名为MyLabel。打开类的代码并插入句子“Inherits Label”,如下面的代码片段所示。编译解决方案。 隐藏,复制Code

Public Class MyLabel
    Inherits Label
End Class 

如果您打开Form1设计器并检查工具箱,您将在顶部发现一个名为MyLabel的新工具。这个控件就像经典的Label控件。你可以把它放在Form1表面上并设置它的属性。它和Label控件没有区别。这是为什么呢?这是因为我们继承了Label控件及其所有功能,换句话说,我们是从Label派生的。 现在,您已经熟悉了创建自己控件的概念,让我们开始使用CustomListBox控件。 新项属性接口 我们实现CustomListBox控件的第一个目标是能够在添加或删除项时生成事件。查看我们的基类ListBox控件,我们可以看到所有项都存储在一个ListBox中。ObjectCollection类,通过Items属性访问。我们在CustomListBox中要做的是创建一个作为新Items接口的新类。这个类将是CustomListBox控件的用户在处理集合时将要访问的类。让我们看一下这个类的代码。 隐藏,收缩,复制Code

Public Class CollectionObjectInterface
       Implements IEnumerable

    Private owner As CustomListBox

    Friend Sub New(ByVal owner As CustomListBox)
        Me.owner = owner
    End Sub

    Public Sub AddRange(ByVal items() As Object)
        For Each item As Object In items
            Me.Add(item)
        Next
    End Sub

    Public Sub AddRange(ByVal value As ListBox.ObjectCollection)
        For Each item As Object In value
            Me.Add(item)
        Next
    End Sub

    Public Sub Add(ByVal item As Object)
        Me.Insert(Me.Count, item)
    End Sub

    Public Sub Insert(ByVal index As Integer, ByVal item As Object)
        Me.owner.InnerItems.Insert(index, item)
        Me.owner.OnItemAdded(index)
    End Sub

    Public Sub Remove(ByVal item As Object)
        Dim index As Integer = Me.IndexOf(item)
        If (index > -1) Then
            Me.RemoveAt(index)
        End If
    End Sub

    Public Sub Clear()
        For i As Integer = (Me.Count - 1) To 0 Step -1
            Me.RemoveAt(i)
        Next
    End Sub

    Public Sub RemoveAt(ByVal index As Integer)
        Me.owner.InnerItems.RemoveAt(index)
        Me.owner.OnItemRemoved(index)
    End Sub

    Public Function Contains(ByVal item As Object) As Boolean
        Return Me.owner.InnerItems.Contains(item)
    End Function

    Public Sub CopyTo(ByVal destination() As Object, ByVal arrayIndex As Integer)
        Me.owner.InnerItems.CopyTo(destination, arrayIndex)
    End Sub

    Public Function GetEnumerator() As System.Collections.IEnumerator _
                    Implements IEnumerable.GetEnumerator
        Return Me.owner.InnerItems.GetEnumerator()
    End Function

    Public Function IndexOf(ByVal value As Object) As Integer
        Return Me.owner.InnerItems.IndexOf(value)
    End Function

    Public ReadOnly Property Count() As Integer
        Get
            Return Me.owner.InnerItems.Count
        End Get
    End Property

    Public Property Item(ByVal index As Integer) As Object
        Get
            Return Me.owner.InnerItems(index)
        End Get
        Set(ByVal value As Object)
            Me.owner.InnerItems(index) = value
        End Set
    End Property

End Class

ObjectCollectionInterface拥有列表框的所有公共成员。ObjectCollection类。你可以说它是列表框的一个拷贝。ObjectCollection类,尽管实现不相同。这是因为我们希望用户能够像使用ListBox控件一样使用Items属性。由于某些原因(超出了本文讨论的范围),我们没有继承ListBox.ObjectCollection。因此,我们必须全面落实所有成员。 这个类的思想是在用户和项目的基类集合之间提供一个层,以便我们可以触发ItemAdded和itemremove事件。它本身不存储任何项,只是调用原始集合的方法。CustomListBox将拥有这个类,因此在构造函数中,我们将一个引用传递给父CustomListBox。CustomListBox将通过一个名为InnerItems的属性公开基类(ListBox)项集合。ObjectCollectionInterface将使用此属性委托正在项集合上执行的所有工作。如您所见,所有与添加或删除项有关的方法调用都将路由到Insert和RemoveAt方法,在这些方法中,我们将对原始集合执行适当的操作,并引发事件。 CustomListBox控制 控件的CustomListBox应负责提供帮助器方法来引发ItemAdded和itemremove事件,实现一个私有属性来将基类项集合公开给ObjectCollectionInterface类,并提供使用ObjectCollectionInterface的项属性的新实现。让我们来看看代码中的这三个目标: 隐藏,收缩,复制Code

''' <summary>
''' Represents a ListBox with events for when adding and removing items. 
''' </summary>
Public Class CustomListBox
    Inherits ListBox

    ' Omitted the code for the internal classes ObjectCollectionInterface and 
    ' ListBoxItemEventArgs...


    Public Event ItemAdded(ByVal sender As Object, ByVal e As ListBoxItemEventArgs)
    Public Event ItemRemoved(ByVal sender As Object, ByVal e As ListBoxItemEventArgs)

    Private itemsInterface As CollectionObjectInterface

    ''' <summary>
    ''' Constructor. Creates a new CollectionObjectInterface that references
    ''' this CustomListBox instance.
    ''' </summary>
    Public Sub New()
        MyBase.New()

        Me.itemsInterface = New CollectionObjectInterface(Me)
    End Sub

    ''' <summary>
    ''' Gets the items of the System.Windows.Forms.CustomListBox.
    ''' </summary>
    ''' <returns>An ObjectCollectionInterface representing the items in 
    ''' the CustomListBox.</returns>
    ''' <remarks>Hides the ListBox Items interface in favor
    '''    of our own interface.</remarks>
    Public Shadows ReadOnly Property Items() As CollectionObjectInterface
        Get
            Return Me.itemsInterface
        End Get
    End Property

    ''' <summary>
    ''' Gets the base Items collection. This property exposes the ListBox's 
    ''' original Items collection to the ObjectCollectionInterface.
    ''' </summary>
    Private ReadOnly Property InnerItems() As ListBox.ObjectCollection
        Get
            Return MyBase.Items
        End Get
    End Property

    ''' <summary>
    ''' Fires the ItemAdded event.
    ''' </summary>
    ''' <paramname="index">Specifies the zero-based index for the item 
    ''' that was added.</param>
    Protected Overridable Sub OnItemAdded(ByVal index As Integer)
        RaiseEvent ItemAdded(Me, New ListBoxItemEventArgs(index))
    End Sub

    ''' <summary>
    ''' Fires the ItemRemoved event.
    ''' </summary>
    ''' <paramname="index">Specifies the zero-based index for the item 
    ''' that was removed.</param>
    Protected Overridable Sub OnItemRemoved(ByVal index As Integer)
        RaiseEvent ItemRemoved(Me, New ListBoxItemEventArgs(index))
    End Sub

End Class

除了继承ListBox控件之外,CustomListBox类最有趣的地方是对Items属性的声明。请记住,我们从基类ListBox继承了Items属性,如果用户要访问ObjectCollectionInterface类,则该属性将没有任何用处。因此,为了利用我们自己的接口,我们必须隐藏基类属性。要隐藏一个基类成员,我们必须使用阴影修改器。new Items属性返回ObjectCollectionInterface的一个实例。实例由构造函数中分配的私有变量持有。 ListBoxItemEventArgs类 您可能已经注意到,在事件声明中,有一个参数e被声明为ListBoxItemEventArgs。该类从EventArgs类派生,并存储一个整数,该整数表示添加或删除的项的从零开始的索引。ListBoxItemEventArgs类的代码如下所示: 隐藏,复制Code

''' <summary>
''' Represents the data for the ItemAdded and ItemRemoved events.
''' </summary>
Public Class ListBoxItemEventArgs
    Inherits EventArgs

    Private _index As Integer

    Public Sub New(ByVal index As Integer)
        Me._index = index
    End Sub

    ''' <summary>
    ''' Gets the zero-based index for the item.
    ''' </summary>
    Public ReadOnly Property Index() As Integer
        Get
            Return Me._index
        End Get
    End Property

End Class

当然,您可以通过一些方法扩展事件的功能。例如,通过向事件数据添加取消标志,可以为事件订阅者添加防止项被删除的可能性。或者,您可以在itemremove事件的事件参数中添加对已删除对象的引用。 关于可撤销成员的说明 在为本文做一些背景工作时,我发现了对微软c#首席架构师Anders Hejlsberg的一次采访,其中讨论了声明基类成员为可重写的优缺点。根据Anders Hejlsberg的说法,. net框架中的大多数类成员不能重写的主要原因有两个。首先,它让重写基本成员的开发人员承担了很多责任。他或她必须知道可以从override成员内部调用哪些其他方法,以及为了不导致无效状态,应该按什么顺序调用这些方法。其次,它引入了调用已被覆盖的类成员的开销。就我个人而言,我发现它限制了不能在某些时候覆盖成员。根据上下文复杂性的不同,我相信。net框架中更多的基类成员可以被标记为可重写,而不会降低整体性能或程序稳定性。在CustomListBox情况下,如果ListBox的所有成员都是这样,事情会变得更好。ObjectCollection标记为可重写。 你可以在这里找到对Anders Hejlsberg的全部采访。 结论 在第一篇文章中,我们了解了如何通过派生一个新控件来扩展或更改现有UI控件的功能。我们实现了一个CustomListBox控件,它可以在添加或删除存储在基类集合中的项时生成事件。在下一篇文章中,我们将研究如何实现在列表中向上或向下移动选定项的方法。 本文转载于:http://www.diyabc.com/frontweb/news357.html

原文地址:https://www.cnblogs.com/Dincat/p/13443766.html