VB.NET: «AddHandler», указав функцию-обработчик в качестве переменной - PullRequest
1 голос
/ 04 апреля 2019

Я пишу надстройку для Autodesk Inventor (программа 3D CAD). Надстройка добавляет набор внутренних команд в Inventor и создает кнопку панели инструментов для каждой команды. Мне нужно подключить событие Execute для каждой из этих команд к функции-обработчику в моей надстройке.

Я создал класс «MyCommand», который содержит всю информацию, необходимую для определения команды. Я также создал список (Of MyCommand) с определениями для каждой команды. Наконец, я могу перебрать каждую команду и создать внутреннее определение команды Inventor, а также добавить кнопку на панель инструментов. Однако я не могу понять, как это сделать, - связать команду с ее функцией-обработчиком внутри цикла .

Пример кода ниже иллюстрирует, что я ищу:

Sub CreateCommands()
    Dim oCommands As New List(Of MyCommand)

    oCommands.Add(New MyCommand("DrawLogoCmd", "Draw Logo", "DrawLogoSub"))
    oCommands.Add(New MyCommand("PublishDrawingCmd", "Publish Drawing", "PublishDrawingSub"))
    ' [Dozens more commands]

    For Each oCommand As MyCommand In oCommands
        ' Code for adding internal command definition and button to Inventor. This is working fine.
        Dim oCommandDef As Inventor.CommandDefinition = InventorApp.CommandDefinitions.Add(oCommand.InternalDefinitionName, oCommand.DisplayName)
        InventorApp.ToolbarButtons.Add(oCommandDef)

        ' Associate command definition with handler function
        ' ===== THIS IS THE LINE I NEED TO FIGURE OUT =====
        AddHandler oCommandDef.OnExecute, AddressOf oCommand.HandlerSubName
    Next
End Sub

Sub DrawLogoSub()
    ' [My add-in's code to draw logo in Inventor]
End Sub

Sub PublishDrawingSub()
    ' [My add-in's code to publish drawing in Inventor]
End Sub

Class MyCommand
    Public InternalDefinitionName As String
    Public DisplayName As String
    Public HandlerSubName As String

    Sub New(InternalDefinitionName As String, DisplayName As String, HandlerSubName As String)
        With Me
            .InternalDefinitionName = InternalDefinitionName
            .DisplayName = DisplayName
            .HandlerSubName = HandlerSubName
        End With
    End Sub
End Class

Итак, возможно ли это? Есть ли какой-нибудь способ получить функцию AddressOf my, используя ее имя в виде строки? Или есть какой-то способ сохранить ссылку на саму функцию в моем классе MyCommand и передать ее оператору AddressOf?

Или, кроме AddHandler / AddressOf, может работать какой-то другой способ?

Любые предложения приветствуются.

Ответы [ 2 ]

4 голосов
/ 04 апреля 2019

Да, вы можете найти метод по имени, используя отражение, но я бы не рекомендовал его. Команде AddHandler обычно присваивается метод AddressOf a, но он также может принимать делегата, если он соответствует сигнатуре события. Поэтому, если у вас нет необходимости делать это по строке, я бы порекомендовал сделать что-то подобное. Предполагая, что событие соответствует обычному EventHandler делегату:

Sub CreateCommands()
    Dim oCommands As New List(Of MyCommand)

    oCommands.Add(New MyCommand("DrawLogoCmd", "Draw Logo", AddressOf DrawLogo))
    oCommands.Add(New MyCommand("PublishDrawingCmd", "Publish Drawing", AddressOf PublishDrawing))
    ' [Dozens more commands]

    For Each oCommand As MyCommand In oCommands
        ' Code for adding internal command definition and button to Inventor. This is working fine.
        Dim oCommandDef As Inventor.CommandDefinition = InventorApp.CommandDefinitions.Add(oCommand.InternalDefinitionName, oCommand.DisplayName)
        InventorApp.ToolbarButtons.Add(oCommandDef)

        ' Associate command definition with handler function
        AddHandler oCommandDef.OnExecute, oCommand.Handler
    Next
End Sub

Sub DrawLogo(sender As Object, e As EventArgs)
    ' [My add-in's code to draw logo in Inventor]
End Sub

Sub PublishDrawing(sender As Object, e As EventArgs)
    ' [My add-in's code to publish drawing in Inventor]
End Sub

Class MyCommand
    Public InternalDefinitionName As String
    Public DisplayName As String
    Public Handler As EventHandler

    Sub New(InternalDefinitionName As String, DisplayName As String, Handler As EventHandler)
        With Me
            .InternalDefinitionName = InternalDefinitionName
            .DisplayName = DisplayName
            .Handler = Handler
        End With
    End Sub
End Class

Если вам действительно нужно сделать это по строке, вы можете использовать отражение следующим образом (обратите внимание, что я использовал NameOf вместо жесткого кодирования имен методов в виде строковых литералов, просто чтобы сделать его более безопасным):

Sub CreateCommands()
    Dim oCommands As New List(Of MyCommand)

    oCommands.Add(New MyCommand("DrawLogoCmd", "Draw Logo", NameOf(DrawLogo)))
    oCommands.Add(New MyCommand("PublishDrawingCmd", "Publish Drawing", NameOf(PublishDrawing)))
    ' [Dozens more commands]

    For Each oCommand As MyCommand In oCommands
        ' Code for adding internal command definition and button to Inventor. This is working fine.
        Dim oCommandDef As Inventor.CommandDefinition = InventorApp.CommandDefinitions.Add(oCommand.InternalDefinitionName, oCommand.DisplayName)
        InventorApp.ToolbarButtons.Add(oCommandDef)

        ' Associate command definition with handler function
        AddHandler oCommandDef.OnExecute, Sub(sender As Object, e As EventArgs) CallMethod(oCommand.Handler)
    Next
End Sub

Private Sub CallMethod(name As String)
    Me.GetType().GetMethod(name).Invoke(Me, Nothing)
End Sub

Sub DrawLogo()
    ' [My add-in's code to draw logo in Inventor]
End Sub

Sub PublishDrawing()
    ' [My add-in's code to publish drawing in Inventor]
End Sub
0 голосов
/ 04 апреля 2019

Вы хотите, чтобы код отображал строку на ссылку метода. Я не на 100% ясен о синтаксисе, но он может выглядеть примерно так:

Dim actionMap As Dictionary(Of string, Action(Of Object, EventArgs))
actionMap.Add("DrawLogo", AddressOf DrawLogo)
actionmap.Add("PublishDrawing", AddressOf PublishDrawing)

И тогда цикл For Each может использовать словарь так:

For Each oCommand As MyCommand In oCommands
    Dim oCommandDef As Inventor.CommandDefinition = InventorApp.CommandDefinitions.Add(oCommand.InternalDefinitionName, oCommand.DisplayName)
    InventorApp.ToolbarButtons.Add(oCommandDef)

    AddHandler oCommandDef.OnExecute, actionMap(oCommand.HandlerSubName)
Next

Пока я здесь, я добавлю переход с VB6 / VBScript на .Net, Microsoft изменила свои рекомендации по стилю для VB. Вместо того, чтобы рекомендовать венгерские бородавки, такие как o, теперь они специально рекомендуют против этих бородавок, основываясь на том, как .Net более активно использует типы и обеспечивает лучшую поддержку инструментов. Префиксы просто не добавляют значение, к которому они привыкли, и теперь вместо oCommand вы можете просто написать command, что, как правило, гораздо более читабельно.

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