Вопрос по vb.net, visual-studio, excel – Как выполнить событие .Onkey в надстройке Excel, созданной в Visual Studio 2010?

8

Я создаю надстройку Excel с использованием Visual Studio 2010. Я хотел бы запустить некоторый код, когда пользователи нажимают комбинацию клавиш.

Вот код, который я получил

<code>Public Class CC

Private Sub ThisAddIn_Startup() Handles Me.Startup
    EnableShortCut()
End Sub

Sub A1()
    MsgBox "A1"
End Sub

Sub A2()
    MsgBox "A2"
End Sub

Sub A3()
    MsgBox "A3"
End Sub

Public Sub EnableShortCut()
    With Application
        .OnKey "+^{U}", "A1"  'action A1 should be performed when user clicks  Ctrl + Shift + U
        .OnKey "+^{L}", "A2"  'action A2 should be performed when user clicks  Ctrl + Shift + L
        .OnKey "+^{P}", "A3"  'action A3 should be performed when user clicks  Ctrl + Shift + P
    End With
End Sub

End Class
</code>

Надстройка при установке показывает ошибку при нажатии ярлыков. Это говорит, что определенный макрос не может быть найден. Код подSub EnableShortCut() хорошо работает, когда он находится в модуле Excel VBA. То же самое не сработает при добавлении в надстройку Excel, созданную с помощью Visual Studio. Кто-нибудь, пожалуйста, помогите мне решить эту проблему.

Ах, хорошо :) AFAIK, и я могу ошибаться, вы не можете использовать.Onkey так, потому что надстройка будет искатьA1 в текущей книге, котораяThisWorkbook, Позвольте мне исследовать, прежде чем я смогу подтвердить это. Siddharth Rout
Пожалуйста, обратитесь кmsdn.microsoft.com/en-us/library/bb608614.aspx Siddharth Rout
В этом случае единственная альтернатива, о которой я могу подумать, - это создать дополнительный плагин (xla / xlam), в котором есть все ваши три сабвуфера, а затем в установщике вашего надстройки выше установить эту вторую надстройку. Я только что проверил, и он работал нормально. Siddharth Rout
@SiddharthRout: вы правы. Но я ищу обходной путь для этого. Если вы что-то получите по этому поводу, это было бы очень полезно. Ian
@SiddharthRout: спасибо за ваш ответ, но это только основы. Код, приведенный здесь, работает нормально. Я просто хочу знать, как обрабатывать событие .Onkey. Ian

Ваш Ответ

4   ответа
0

Create .xlsm file and add these VBA code

Sub AOne()
MsgBox "Message from AOne"
End Sub

Sub ATwo()
MsgBox "Message from ATwo"
End Sub

Sub AThree()
MsgBox "Message from AThree"
End Sub

Now create a Excel Workbook project in Visual studio and add existing file and choose the above created .xlsm file

private void ThisWorkbook_Startup(object sender, System.EventArgs e)
{
    EnableShortCut();
}

private void ThisWorkbook_Shutdown(object sender, System.EventArgs e)
{
}

public void EnableShortCut()
{
    Excel.Application app = Globals.ThisWorkbook.Application;
    app.OnKey("+^{U}", "AOne"); //action A1 should be performed when user clicks  Ctrl + Shift + U
    app.OnKey("+^{L}", "ATwo");//action A2 should be performed when user clicks  Ctrl + Shift + L
    app.OnKey("+^{P}", "AThree"); //action A3 should be performed when user clicks  Ctrl + Shift + P
}

Запустите ваш проект, это должно сработать, Application.OnKey в Excel или Application.Keybindings в Word принимает имя макроса в качестве параметра.

Проверьте мой ответ для использования сочетаний клавиш в WordВот

Я думаю, что внимательно прочитал вопрос, вы упустили возможность сказать, является ли это надстройкой уровня приложения или надстройкой уровня документа. Я ответил, что вы работаете над надстройкой на уровне документа. Не берите в голову, вы можете выполнить то же самое, скопировав код VBA в Book.xltx в свою папку XLStart или создайте его.
пожалуйста, прочитайте абзац ниже кода. Ian
Пожалуйста, внимательно прочитайте вопрос. Я не делаю это в книге Excel. Я создаю надстройку с использованием Visual Studio. Метод, который вы описали, заключается в создании рабочей книги. Разница огромная. У меня была похожая проблема, которую я разместил здесьstackoverflow.com/questions/10151345/…, В этом случае я смог найти решение с помощью помощи. Ian
2

метод Application.OnKey () очень ограничен. Он может вызывать только макрос и не может передавать аргументы. Это означает, что вы будетеhave предоставить набор макросов. Вам не нужны конкретные книги, вам нужны макросы, которые работают в любом документе. Давайте сначала займемся этим.

Start Excel and use File + Close to close the default workbook Click Record macro. Change the "Store macro in" setting to Personal Macro Workbook OK to close the dialog and click Stop Recording Click Visual Basic. Note that you've now got a VBA project named PERSONAL.XLSB with a Module1

Удалите Macro1 и скопируйте / вставьте этот код VBA:

Sub MyAddinCommand1()
  Application.COMAddIns("ExcelAddin1").Object.Command 1
End Sub

Sub MyAddinCommand2()
  Application.COMAddIns("ExcelAddin1").Object.Command 2
End Sub

Sub MyAddinCommand3()
  Application.COMAddIns("ExcelAddin1").Object.Command 3
End Sub

Повторите столько раз, сколько необходимо, вы хотите один для каждой клавиши быстрого доступа, которую вы хотите определить. Нажмите Сохранить. Теперь вы создали файл в каталоге c: \ users \ yourname \ appdata \ roaming \ microsoft \ excel \ xlstart \ personal.xlsb, содержащий макросы. Любой, кто собирается использовать ваше расширение, также должен иметь этот файл, подробности развертывания.

Следующее, что вам нужно сделать, это выставить свои команды. Sub A1 (), который вы написали в надстройке, не подойдет, методы должны быть представлены как методы видимого COM-класса. Добавьте новый класс в ваш проект и сделайте код похожим на это:

Imports System.Runtime.InteropServices

<InterfaceType(Runtime.InteropServices.ComInterfaceType.InterfaceIsIDispatch)> _
<ComVisible(True)> _
Public Interface IMyAddinCommand
    Sub Command(ByVal index As Integer)
End Interface

<ClassInterface(Runtime.InteropServices.ClassInterfaceType.None)> _
<ComVisible(True)> _
Public Class MyAddinCommand
    Implements IMyAddinCommand

    Public Sub Command(index As Integer) Implements IMyAddinCommand.Command
        MsgBox("Command #" + CStr(index))
    End Sub
End Class

Просто простой, который предоставляет единственный метод с именем Command (), который принимает целое число. Я только что использовал MsgBox, вы хотите написать оператор Select для реализации команды, основанной на значенииindex, Также обратите внимание на совпадение с кодом в глобальном макросе.

Еще одна вещь, которую вам нужно сделать, вы должны явно предоставить этот класс. Переопределите функцию RequestComAddInAutomationService в вашем дополнении. Тот, который я использовал для проверки, выглядел так:

Public Class ThisAddIn

    Private Sub ThisAddIn_Startup() Handles Me.Startup
        Application.OnKey("+^{U}", "Personal.xlsb!MyAddinCommand1")  '' Ctrl + Shift + U
    End Sub

    Protected Overrides Function RequestComAddInAutomationService() As Object
        If commands Is Nothing Then commands = New MyAddinCommand
        Return commands
    End Function

    Private commands As MyAddinCommand

End Class

Нажмите F5 для компиляции и запуска Excel. Когда я нажимаю Ctrl + Shift + U, я получаю это:

enter image description here

@HansPassant Могу ли я получить ваше быстрое мнение о решении, которое я опубликовал в плане безопасности, спасибо
спасибо, давайте посмотрим, что может предложить Excel-DNA @Govert.
Опять же, я не хочу использовать любой другой файл, кроме надстройки vsto. Ian
Это нереальный вариант, вам действительно нужен макрос. Вы можете скопировать файл на место из ресурса в вашем событии запуска, но это может привести к серьезным проблемам с ИТ-персоналом. Кто не увидит разницы с инъекцией макровируса. Вы можете легко положить его в нужном месте с помощью проекта установки.
@ Джереми - такая же проблема, никто не может сказать, что это не регистратор ключей.
2

Excel-DNA (библиотека интеграции с открытым исходным кодом .NET / Excel, которую я разрабатываю), методы и пользовательские функции в вашем .NET-коде регистрируются в Excel через C API. В результате поведение ближе к VBA, и ваш код с Application.OnKey & quot; ... & quot; струны тоже подойдут.

Excel-DNA позволяет вашему коду находиться в скомпилированной сборке .NET .dll или непосредственно в виде текста в формате .dna. файл, который обрабатывается при загрузке надстройки. Вот пример такого текстового файла (код выглядел бы так же, как если бы он был в скомпилированном проекте). Как упоминалось в одном из других ответов, я переименовал макросы, чтобы их имена не конфликтовали с именами ячеек A1 и т. Д.

Сделать надстройку

save the code as a text file called OnKeyTest.dna, and match it with a copy of the Excel-DNA host library ExcelDna.xll from the release on CodePlex, which you just copy and rename to OnKeyTest.xll (the matching name is how the .dna file is found when you load the .xll).

Эти два файла сформировали бы вашу полную надстройку, просто нужную для запуска на компьютере .NET.

<DnaLibrary Language="VB" RuntimeVersion="v2.0" >
<![CDATA[
Imports ExcelDna.Integration

Public Class MyAddIn
    Implements IExcelAddIn

    Private Sub AutoOpen() Implements IExcelAddIn.AutoOpen
        EnableShortCut()
    End Sub

    Private Sub AutoClose() Implements IExcelAddIn.AutoClose
    End Sub

    Sub EnableShortCut()
        With ExcelDnaUtil.Application
            .OnKey("+^{U}", "MacroA1")  'action A1 should be performed when user clicks  Ctrl + Shift + U
            .OnKey("+^{L}", "MacroA2")  'action A2 should be performed when user clicks  Ctrl + Shift + L
            .OnKey("+^{P}", "MacroA3")  'action A3 should be performed when user clicks  Ctrl + Shift + P
        End With
    End Sub
End Class

Public Module MyMacros
    Sub MacroA1()
        MsgBox("A1")
    End Sub

    Sub MacroA2()
        MsgBox("A2")
    End Sub

    Sub MacroA3()
        MsgBox("A3")
    End Sub
End Module
]]>
</DnaLibrary>
ОК - пример кода и добавлены некоторые инструкции.
Не могли бы вы опубликовать пример кода? заранее спасибо
5

"I would like to run some code when users presses a combination of keys."

Its tricky and to do it without any external dependencies resort to Keyboard hooking to achieve it with a VSTO Excel Add-in:

Imports System
Imports System.Runtime.CompilerServices
Imports System.Runtime.InteropServices
Imports System.Windows.Forms

Friend Class KeyboardHooking
    ' Methods
    <DllImport("user32.dll", CharSet:=CharSet.Auto, SetLastError:=True)> _
    Private Shared Function CallNextHookEx(ByVal hhk As IntPtr, ByVal nCode As Integer, ByVal wParam As IntPtr, ByVal lParam As IntPtr) As IntPtr
    End Function

    <DllImport("kernel32.dll", CharSet:=CharSet.Auto, SetLastError:=True)> _
    Private Shared Function GetModuleHandle(ByVal lpModuleName As String) As IntPtr
    End Function

    Private Shared Function HookCallback(ByVal nCode As Integer, ByVal wParam As IntPtr, ByVal lParam As IntPtr) As Integer
        If ((nCode >= 0) AndAlso (nCode = 0)) Then
            Dim keyData As Keys = DirectCast(CInt(wParam), Keys)
            If (((BindingFunctions.IsKeyDown(Keys.ControlKey) AndAlso BindingFunctions.IsKeyDown(Keys.ShiftKey)) AndAlso BindingFunctions.IsKeyDown(keyData)) AndAlso (keyData = Keys.D7)) Then
'DO SOMETHING HERE
            End If
            If ((BindingFunctions.IsKeyDown(Keys.ControlKey) AndAlso BindingFunctions.IsKeyDown(keyData)) AndAlso (keyData = Keys.D7)) Then
'DO SOMETHING HERE
            End If
        End If
        Return CInt(KeyboardHooking.CallNextHookEx(KeyboardHooking._hookID, nCode, wParam, lParam))
    End Function

    Public Shared Sub ReleaseHook()
        KeyboardHooking.UnhookWindowsHookEx(KeyboardHooking._hookID)
    End Sub

    Public Shared Sub SetHook()
        KeyboardHooking._hookID = KeyboardHooking.SetWindowsHookEx(2, KeyboardHooking._proc, IntPtr.Zero, Convert.ToUInt32(AppDomain.GetCurrentThreadId))
    End Sub

    <DllImport("user32.dll", CharSet:=CharSet.Auto, SetLastError:=True)> _
    Private Shared Function SetWindowsHookEx(ByVal idHook As Integer, ByVal lpfn As LowLevelKeyboardProc, ByVal hMod As IntPtr, ByVal dwThreadId As UInt32) As IntPtr
    End Function

    <DllImport("user32.dll", CharSet:=CharSet.Auto, SetLastError:=True)> _
    Private Shared Function UnhookWindowsHookEx(ByVal hhk As IntPtr) As <MarshalAs(UnmanagedType.Bool)> Boolean
    End Function


    ' Fields
    Private Shared _hookID As IntPtr = IntPtr.Zero
    Private Shared _proc As LowLevelKeyboardProc = New LowLevelKeyboardProc(AddressOf KeyboardHooking.HookCallback)
    Private Const WH_KEYBOARD As Integer = 2
    Private Const WH_KEYBOARD_LL As Integer = 13
    Private Const WM_KEYDOWN As Integer = &H100

    ' Nested Types
    Public Delegate Function LowLevelKeyboardProc(ByVal nCode As Integer, ByVal wParam As IntPtr, ByVal lParam As IntPtr) As Integer
End Class

Public Class BindingFunctions
    ' Methods
    <DllImport("user32.dll")> _
    Private Shared Function GetKeyState(ByVal nVirtKey As Integer) As Short
    End Function

    Public Shared Function IsKeyDown(ByVal keys As Keys) As Boolean
        Return ((BindingFunctions.GetKeyState(CInt(keys)) And &H8000) = &H8000)
    End Function

End Class

C# version - the original that the above vb.net code was converted from - but I had to use Reflector as CodeConverter & devfusion didn't do it correctly.

class KeyboardHooking
{
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern IntPtr SetWindowsHookEx(int idHook, LowLevelKeyboardProc lpfn, IntPtr hMod,
    uint dwThreadId);

[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool UnhookWindowsHookEx(IntPtr hhk);

[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern IntPtr CallNextHookEx(IntPtr hhk, int nCode, IntPtr wParam, IntPtr lParam);

[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern IntPtr GetModuleHandle(string lpModuleName);

public delegate int LowLevelKeyboardProc(int nCode, IntPtr wParam, IntPtr lParam);
private static LowLevelKeyboardProc _proc = HookCallback;
private static IntPtr _hookID = IntPtr.Zero;

//declare the mouse hook constant.
//For other hook types, you can obtain these values from Winuser.h in the Microsoft SDK.

private const int WH_KEYBOARD = 2; // mouse
private const int HC_ACTION = 0;

private const int WH_KEYBOARD_LL = 13; // keyboard
private const int WM_KEYDOWN = 0x0100;

public static void SetHook()
{
    // Ignore this compiler warning, as SetWindowsHookEx doesn't work with ManagedThreadId
    #pragma warning disable 618
    _hookID = SetWindowsHookEx(WH_KEYBOARD, _proc, IntPtr.Zero, (uint)AppDomain.GetCurrentThreadId());
    #pragma warning restore 618

}

public static void ReleaseHook()
{
    UnhookWindowsHookEx(_hookID);
}

//Note that the custom code goes in this method the rest of the class stays the same.
//It will trap if BOTH keys are pressed down.
private static int HookCallback(int nCode, IntPtr wParam, IntPtr lParam)
{
    if (nCode < 0)
    {
        return (int)CallNextHookEx(_hookID, nCode, wParam, lParam);
    }
    else
    {

        if (nCode == HC_ACTION)
        {
            Keys keyData = (Keys)wParam;

            // CTRL + SHIFT + 7
            if ((BindingFunctions.IsKeyDown(Keys.ControlKey) == true)
                && (BindingFunctions.IsKeyDown(Keys.ShiftKey) == true)
                && (BindingFunctions.IsKeyDown(keyData) == true) && (keyData == Keys.D7))
            {
                // DO SOMETHING HERE
            }

            // CTRL + 7
            if ((BindingFunctions.IsKeyDown(Keys.ControlKey) == true)
                && (BindingFunctions.IsKeyDown(keyData) == true) && (keyData == Keys.D7))
            {
                // DO SOMETHING HERE
            }



        }
        return (int)CallNextHookEx(_hookID, nCode, wParam, lParam);
    }
}
}

public class BindingFunctions
{
[DllImport("user32.dll")]
static extern short GetKeyState(int nVirtKey);

public static bool IsKeyDown(Keys keys)
{
    return (GetKeyState((int)keys) & 0x8000) == 0x8000;
}

}

You'll need to put code in the HookCallback() method in the above code to trap events when key combinations are pressed, I've given you two examples Ctrl + Shift + 7 and Ctrl + 7 to get you going.

Затем в вашем Excel AddIn подключите его:

Private Sub ThisAddIn_Startup() Handles Me.Startup

'enable keyboard intercepts
KeyboardHooking.SetHook()

И не забудьте отключить его, когда вы закончите:

Private Sub ThisAddIn_Shutdown() Handles Me.Shutdown

'disable keyboard intercepts
KeyboardHooking.ReleaseHook()
@ Пожалуйста, попробуйте исправленный код. Я проверил это - причина, по которой он не работал раньше, заключалась в том, что я преобразовал его из C # в VB.Net, но у преобразователей кода были проблемы, ура
Это также помогло мне Джереми, спасибо. Один вопрос - любая идея, как заставить его переиграть обычные клавиши. Например, Ctrl + Shift + C, выполнит команду надстройки, но все равно наберет & quot; c & quot; в открытый документ Excel ячейки / слова.
Просто отвечая на мой собственный глупый вопрос: чтобы переопределить предыдущие нажатия клавиш, вам нужно вернуть значение, чтобы функция не зацикливалась на следующем хуке. Делать что-то вроде & quot; вернуть -1 & quot; после вашей команды достаточно переопределить.
@ Джерми Томпсон: это все ... спасибо ... щедрость принадлежит тебе ... Ian
Это не работает. Ian

Похожие вопросы