Использование делегатов для обработки события - PullRequest
4 голосов
/ 07 мая 2009

Извините, это лучший вопрос, который я могу придумать, если бы я лучше понял решение, я мог бы, вероятно, сформулировать лучшую тему.

Я использую отличный элемент управления сеткой, Super List, l расположен здесь:

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

---------------------------

А вот сигнатуры метода-экземпляра столбца и определение последнего параметра, который отвечает за указание процедуры, которая обрабатывает, идентифицирует обработчик события, отвечающий за возврат значения столбца.

Public Sub New (имя ByVal как строка, заголовок ByVal как строка, ширина ByVal как целое число, ByVal columnItemValueAccessor как BinaryComponents.SuperList.ColumnItemValueAccessor) Член BinaryComponents.SuperList.Column

Public Sub New (объект ByVal как объект, метод ByVal как System.IntPtr) Член BinaryComponents.SuperList.ColumnItemValueAccessor


У кого-нибудь есть предложения или я застрял? Мне бы очень хотелось использовать возможности этого элемента управления для фантастической группировки, чтобы я мог отображать динамический вывод, который позволяет пользователю группировать динамический вывод SQL по любому столбцу, который он хочет.

Я адресовал вопрос автору на вышеуказанном сайте, но он остался без ответа. Это отчаянная попытка найти способ сделать это.

Спасибо за терпение со мной. Я надеюсь, что этот вопрос не будет отклонен на основании того, что я имею в виду контроль третьей стороны. Я надеюсь, что ответ заключается в лучшем понимании делегатов, в более универсальной теме.

Ответы [ 2 ]

4 голосов
/ 16 мая 2009

Я использовал лямбда-функцию, как предложил Мэтью. Вот код из динамического подхода:

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

Вот краткий пример того, как это работает:

Каждый раз в цикле лямбда-функция создает новую функцию обратного вызова для каждого столбца, которая выглядит примерно так:

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 в цикле, а не использовать только 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 голос
/ 07 мая 2009

Проблема в том, что функция ItemValueAccessor имеет сигнатуру, которая включает только объект строки, и я не знаю, как определить, какое имя столбца необходимо, поскольку все столбцы подключены к той же функции ItemValueAccessor, что и событие. обработчик.

Хорошо, я не использовал этот контроль в прошлом, и я действительно человек C #. Но я думаю, что вы можете достичь этого, создав новую лямбда-функцию для каждого столбца. Что-то вроде:

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 получает необходимую информацию. Полностью не проверено, но я думаю, что основная идея работает.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...