使用Delegates处理事件

时间:2009-05-07 04:44:03

标签: vb.net events delegates

很抱歉,这是我能提出的最好的主题,如果我更好地理解解决方案,我可能会说出更好的主题。

我正在使用一个很棒的网格控件,超级列表,位于此处:

http://www.codeproject.com/KB/list/outlooklistcontrol.aspx?fid=449232&df=90&mpp=25&noise=3&sort=Position&view=Quick&fr=276

在您阅读此问题之前,请注意您可以下载一个非常小的VB.NET 2005示例应用程序来解决问题:

http://dokmanovich.com/Documents/SuperListEvents.zip

我希望能够回答我的问题,帮助我更好地理解我想要完成的动态事件。

网格的工作方式如下:向网格添加列时,指定将在运行时返回值的事件处理程序的地址。在这种情况下,CC_ItemValueAccessor函数。后一个函数将使用输入参数调用,在这种情况下,该参数是“ToDo”对象。每个ToDo对象将在网格中呈现为一行。 CC_ItemValueAccessor函数的作用是返回网格显示的列值,该列对应于传入的ToDo对象的行。

这一切正常,直到我把它带到下一步:

我希望动态在运行时创建列。例如,我想显示由于执行用户指定的SQL而返回的数据表的输出。

使用前面描述的静态方法,我有一个columnItemValueAccessor函数负责返回传入的行对象的网格中每列的值。现在,由于列是在运行时根据SQL返回的结果确定的,我相信我需要编写一个处理所有列的通用处理程序,确定触发此事件的列的名称,然后在其中返回该列的值作为唯一参数传入的行对象。

问题是ItemValueAccessor函数有一个只包含行对象的签名,我不知道如何确定需要哪个列名,因为所有列都连接到与事件相同的ItemValueAccessor函数处理程序。

我怀疑这只是控件的一个限制,为了克服这个问题,我必须增强底层的自定义控件,但这可能超出了我目前的技能,因为它是用C#编写的高级控件而且我是一个VB人。

以下是代码:

Private Sub AddCcColumn()
    Dim NewColumn As New BinaryComponents.SuperList.Column("CC", "CC", 110, AddressOf Cc_ItemValueAccessor)
    _SuperList.Columns.Add(NewColumn)
End Sub

Private Function Cc_ItemValueAccessor(ByVal rowItem As Object) As Object
    Dim ToDo As ToDo = CType(rowItem, SrToDoAndException).ToDo
    Return ToDo.CCs.ToString
End Function

“---------------------------

以下是Column的instantiator方法的签名以及最后一个参数的定义,该参数负责指定处理的过程,以识别负责返回列值的事件处理程序。

Public Sub New(ByVal name As String,ByVal caption As String,ByVal width As Integer,ByVal columnItemValueAccessor As BinaryComponents.SuperList.ColumnItemValueAccessor)      BinaryComponents.SuperList.Column

的成员

Public Sub New(ByVal对象As Object,ByVal方法As System.IntPtr)      BinaryComponents.SuperList.ColumnItemValueAccessor

的成员

有人有任何建议或者我被卡住了吗?我真的很想利用这个控件的幻想分组功能,所以我可以显示动态输出,允许用户按照他们想要的任何列对SQL的动态输出进行分组。

我在上述网站向作者提出了这个问题,但是没有得到答复。这是一种绝望的尝试,希望找到一种方法。

谢谢你的支持。我希望这个问题不会因为我提到第三方控制而被拒绝。我希望答案在于更好地理解代表,这是一个更普遍的话题。

2 个答案:

答案 0 :(得分:4)

我使用了lambda函数,正如Matthew建议的那样。以下是动态方法的代码:

Private Sub btnDynamic_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnDynamic.Click

    ListControl1.Columns.Clear()

    For Each DataCol As DataColumn In _ds.dtbPerson.Columns
        ' Get the column name in a loop variable - it needs to be in loop scope or this won\'t work properly'
        Dim colName = DataCol.ColumnName
        ' Create the function that will be called by the grid'
        Dim colLambda As ColumnItemValueAccessor = Function(rowItem As Object) General_ItemValueAccessor(rowItem, colName)
        ' Setup each column in the grid'
        Dim NewColumn As New BinaryComponents.SuperList.Column(DataCol.ColumnName, DataCol.ColumnName, 220, colLambda)
        ListControl1.Columns.Add(NewColumn)
    Next

End Sub

Private Function General_ItemValueAccessor(ByVal rowItem As Object, ByVal colName As Object) As Object
    Dim rowPerson As DataRow = CType(rowItem, DataRow)
    Return rowPerson.Item(colName).ToString
End Function

以下是它如何运作的快速入门:

每次循环时,lambda函数都会为每个列创建一个新的回调函数,如下所示:

Class Func1
    Dim colName1 As String = "PersonId"

    Private Function General_ItemValueAccessor1(ByVal rowItem As Object) As Object
        Dim rowPerson As DataRow = CType(rowItem, DataRow)
        Return rowPerson.Item(Me.colName1).ToString
    End Function
End Class

Class Func2
    Dim colName2 As String = "LastName"

    Private Function General_ItemValueAccessor2(ByVal rowItem As Object) As Object
        Dim rowPerson As DataRow = CType(rowItem, DataRow)
        Return rowPerson.Item(Me.colName2).ToString
    End Function
End Class

... for however many columns you have - 3 in this case.

您需要循环中的colName变量,而不是直接在lambda中使用DataCol.ColumnName。否则,当网格到处调用回调函数时,该DataCol变量将等于集合中的最后一个值(或Nothing),用于所有回调函数。

基本上,它会这样做,你不会得到你所期望的:

Class Func
    Dim DataCol1 = DataCol

    Private Function General_ItemValueAccessor1(ByVal rowItem As Object) As Object
        Dim rowPerson As DataRow = CType(rowItem, DataRow)
        Return rowPerson.Item(Me.DataCol1.ColumnName).ToString
    End Function

    Private Function General_ItemValueAccessor2(ByVal rowItem As Object) As Object
        Dim rowPerson As DataRow = CType(rowItem, DataRow)
        Return rowPerson.Item(Me.DataCol1.ColumnName).ToString
    End Function
    ...

End Class

希望有所帮助。祝你好运。

答案 1 :(得分:1)

  

问题是ItemValueAccessor函数有一个只包含行对象的签名,我不知道如何确定需要哪个列名,因为所有列都连接到与事件相同的ItemValueAccessor函数处理程序。

好吧,我过去没有使用过这个控件,而且我真的是一个C#人。但我认为您可以通过为每列创建新的lambda function来实现此目的。类似的东西:

Private Sub AddCcColumn(ByVal sender As System.Object As System.String)
    colLambda = (Function(rowItem As Object) Cc_InternalItemValueAccessor(columnName, rowItem))
    Dim NewColumn As New BinaryComponents.SuperList.Column("CC", "CC", 110, colLambda)
    _SuperList.Columns.Add(NewColumn)
End Sub

然后,colLambda将适合签名,而您的内部Cc_InternalItemValueAccessor将获得所需的信息。完全未经测试,但我认为基本想法有效。