Search code examples
wpfkey-bindingscommandbindingvs-extensibilityvspackage

How do you prevent the VS Shell from getting priority on key/command bindings in a VSPackage ToolWindow?


I have wpf a user control that accepts input gestures to perform various commands. In the example image below, the user can press Ctrl+N to execute the New command to create a new item in the TreeView.

enter image description here

When this user control is hosted within a WPF application, the user can create a new item using Ctrl+N when the focus is on the TreeView, and when the user presses Ctrl+N when the focus is elsewhere in the application, the application level New command is executed.

When this user control is hosted within a VSPackage ToolWindow, the VS Shell level New command to create a new file is executed when the user presses Ctrl+N, regardless of focus on the TreeView.

How do you prevent the VS Shell from getting priority on key/command bindings?


Solution

  • Some approaches were suggested by moderator Ryan on MSDN. The VS Shell is passing Winform messages to the ToolWindow, so the current approach is to override PreProcessMessage in the ToolWindow, handle the key combinations such as Ctrl+N that would map to WPF commands, and then translate those messages and pass to ComponentDispatcher.RaiseThreadMessage.

    This approach is error prone in that the mapping to WPF commands could get out of sync, plus it doesn't incorporate whether or not the WPF command can execute. If anyone has a more ideal approach, please let me know, or if I find one, will post.

    Perhaps there is a way for the WPF control to pass back whether the command was executed, and then only if the command was executed would the message be handled. Then, all this mapping business could be avoided.

        private bool isControlKeyDepressed = false;
        private bool isShifKeyDepressed = false;
        private bool isOtherKeyDepressed = false;
        private bool isCommandCombinationDepressed = false;
    
        protected override bool PreProcessMessage(ref Message msg)
        {
            // trap keyboard messages if window has focus
            if (msg.Msg == 256)
            {
                if (msg.WParam == (IntPtr)17)
                {
                    isControlKeyDepressed = true;
                    isOtherKeyDepressed = false;
                }
                else if (msg.WParam == (IntPtr)16)
                {
                    isShifKeyDepressed = true;
                    isOtherKeyDepressed = false;
                }
                else
                {
                    if (isOtherKeyDepressed == true)
                    {
                        isControlKeyDepressed = false;
                        isShifKeyDepressed = false;
                    }
                    isOtherKeyDepressed = true;
                    if (isControlKeyDepressed == true)
                    {
                        if (isShifKeyDepressed == true)
                        {
                            switch (msg.WParam.ToInt64())
                            {
                                case 65: // Ctrl+Shift+A command
                                case 67: // Ctrl+Shift+C command
                                case 78: // Ctrl+Shift+N command
                                case 79: // Ctrl+Shift+O command
                                case 83: // Ctrl+Shift+S command
                                case 85: // Ctrl+Shift+U command
                                case 88: // Ctrl+Shift+X command
                                    isCommandCombinationDepressed = true;
                                    break;
                                default:
                                    isCommandCombinationDepressed = false;
                                    break;
                            }
                        }
                        else
                        {
                            switch (msg.WParam.ToInt64())
                            {
                                case 69: // Ctrl+E command
                                case 78: // Ctrl+N command
                                case 79: // Ctrl+O command
                                case 83: // Ctrl+S command
                                    isCommandCombinationDepressed = true;
                                    break;
                                default:
                                    isCommandCombinationDepressed = false;
                                    break;
                            }
                        }
                    }
                    else
                    {
                        isCommandCombinationDepressed = false;
                    }
                }
    
                if (isCommandCombinationDepressed == true)
                {
                    // send translated message via component dispatcher
                    MSG dispatchMsg = new MSG();
                    dispatchMsg.hwnd = msg.HWnd;
                    dispatchMsg.lParam = msg.LParam;
                    dispatchMsg.wParam = msg.WParam;
                    dispatchMsg.message = msg.Msg;
                    ComponentDispatcher.RaiseThreadMessage(ref dispatchMsg);
                    msg.Result = (IntPtr)1;
                    return true;
                }
            }
            return base.PreProcessMessage(ref msg);
        }