views:

325

answers:

2

Sorry, that's the best subject I can come up with, if I understood the solution better, I could probably phrase a better subject line.

I am using a great grid control, Super List,l located here:

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

Before you read the problem, please note that you can download a very small VB.NET 2005 sample app which demos the problem:

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

Getting the answer to my question will, I hope, help me to understand dynamic events better in the context of what I am trying to accomplish.

The grid works like this: When you add a column to the grid, you specify the address of an event handler which will return the value at run time. In this case, the CC_ItemValueAccessor function. The latter function will be called with an input parameter which, in this case, is a "ToDo" object. Each ToDo object will be rendered as one row in the grid. The job of the CC_ItemValueAccessor function is to return the column value to be displayed by the grid for the row that corresponds to the passed-in ToDo object.

This works fine till I take it to the next step:

I want to dynamically create columns at run time. For example, I want to display the output of a datatable returned as a result of executing a user-specified SQL.

Using the earlier described static approach, I have one columnItemValueAccessor function responsible for returning the value of each column in the grid for the passed in row object. Now, since the columns are determined at run time based on the SQL returned results, I believe I need to write a generic handler that handles all columns, determines the name of the column that triggered this event and then returns the value for that column within the row object that is passed in as the sole parameter.

The problem is that the ItemValueAccessor function has a signature that only includes the row object and I do not know of a way to determine which column name is needed since all of the columns were hooked up to the same ItemValueAccessor function as the event handler.

I suspect that this is just a limitation of the control and that to overcome this problem I would have to enhance the underlying custom control, but that is likely beyond my current skills as it is an advanced control written in C# and I am a VB guy.

Here's the code:

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

'---------------------------

And here are the signatures of the Column's instantiator method and the definition of the last parameter which is responsible for specifying the procedure that handles identifies the event handler responsible for returning the value of the column.

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

Public Sub New(ByVal object As Object, ByVal method As System.IntPtr) Member of BinaryComponents.SuperList.ColumnItemValueAccessor


Does anyone have any suggestions or am I stuck? I would really love to utilize the fantasic grouping capabilities of this control so I can display dynamic output that allows the user to group the dynamic output of a SQL by any column that they want.

I addressed the question to the author at the above site but it has gone unanswered. This is a desperate attempt to find a way to do this.

Thanks for bearing with me. I hope this question isn't rejected based on the fact that I refer to a third party control. My hope is that the answer lies in a better understanding of delegates, a more universal topic.

+1  A: 

The problem is that the ItemValueAccessor function has a signature that only includes the row object and I do not know of a way to determine which column name is needed since all of the columns were hooked up to the same ItemValueAccessor function as the event handler.

Okay, I haven't used that control in the past, and I'm really a C# person. But I think you may be able to accomplish this by creating a new lambda function for each column. Something like:

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

Then, colLambda will fit the signature, while your internal Cc_InternalItemValueAccessor gets the info it needs. Totally untested, but I think the basic idea works.

Matthew Flaschen
Hmmm. I know know anything about lambda expressions, other than it is new and prehaps related to LINQ (and anonymous types?) I'll see what I can find. Thanks for the reply.
Velika
I think I need a VB guy that understands Lambas to spoon feed it to me.
Velika
+2  A: 

I used a lambda function, as Matthew suggested. Here is the code from the dynamic approach:

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

Here's a quick primer on how it works:

Each time through the loop the lambda function is creating a new callback function for each column that looks something like this:

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.

You need the colName variable within the loop and not use just DataCol.ColumnName directly in the lambda. Otherwise, when the grid gets around to calling the callback functions, that DataCol variable would be equal to the last value from the collection (or Nothing) for all the callback functions.

Basically, it would do this and you wouldn't get what you expected:

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

Hope that helps. Good luck.

Steven Lyons
Awesome, thank you. It worked like a charm. I would've have like to given Matthew credit too (I voted up his answer), but you were the one that helped me to understand and get it actually working. Thank you!
Velika