【问题标题】:Using Delegates for handling an event使用委托处理事件
【发布时间】:2009-05-07 04:44:03
【问题描述】:

抱歉,这是我能想到的最好的主题,如果我能更好地理解解决方案,我可能会写出更好的主题行。

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

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 的实例化方法的签名和最后一个参数的定义,该参数负责指定处理标识负责返回列值的事件处理程序的过程。

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 object As Object, ByVal method As System.IntPtr) BinaryComponents.SuperList.ColumnItemValueAccessor 的成员


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

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

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

【问题讨论】:

  • Lamba 评论很有趣,但我需要更多帮助。我创建了一个工作示例,将问题简化为基本问题,并添加了一条说明需要什么的注释。如果你愿意的话,你可以在这里得到它:dokmanovich.com/Documents/SuperListEvents.zip 否则,我只是在寻找比伪代码更好的东西和一个大方向的点,因为我自己似乎无法弄清楚。我提供赏金,从 50 开始。

标签: vb.net events delegates


【解决方案1】:

按照 Matthew 的建议,我使用了 lambda 函数。这是动态方法的代码:

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

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

【讨论】:

  • 太棒了,谢谢。它就像一个魅力。我也想给 Matthew 一个信用(我投票赞成他的回答),但你是帮助我理解并让它真正发挥作用的人。谢谢!
【解决方案2】:

问题是 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 会获取它需要的信息。完全未经测试,但我认为基本想法可行。

【讨论】:

  • 嗯。我知道关于 lambda 表达式的任何事情,除了它是新的并且可能与 LINQ(和匿名类型?)有关,我会看看我能找到什么。感谢您的回复。
  • 我想我需要一个懂 Lambas 的 VB 人来给我喂食。
猜你喜欢
  • 2019-05-16
  • 1970-01-01
  • 1970-01-01
  • 2013-08-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多