【问题标题】:How do I override Ctrl+Shift+0 (zero) for WinForms RichTextBox?如何为 WinForms RichTextBox 覆盖 Ctrl+Shift+0(零)?
【发布时间】:2013-11-02 14:05:35
【问题描述】:

对于我的子类RichTextBox 类,我可以捕获、抑制默认行为并重新使用 Ctrl+Shift+#,只要 # 介于 1 和 9 之间。对于 Ctrl+Shift+0,我不能.我在表单类中尝试了ProcessCmdKey,在控件类中尝试了onKeyDown和PreProcessMessage。

这里是用于应该抑制 Ctrl+Shift+0 但不抑制的控件类的示例代码:

public override bool PreProcessMessage(ref Message msg)
{
    bool cancel = false;
    int vKeyCode = (int)msg.WParam;

    if(msg.Msg == WinApi.WM_KEYDOWN)
    {
        bool ctrlDown = (WinApi.GetKeyState(Keys.ControlKey) & (1 << 16)) == (1 << 16);
        bool altDown = (WinApi.GetKeyState(Keys.Alt) & (1 << 16)) == (1 << 16);
        bool shiftDown = (WinApi.GetKeyState(Keys.ShiftKey) & (1 << 16)) == (1 << 16);

        if(ctrlDown && shiftDown && vKeyCode == (int)Keys.D0)
        {
            Debug.WriteLine("Cancel!");
            cancel = true;
        }
    }

    return cancel ? true : base.PreProcessMessage(ref msg);
}

但是,将 Keys.D0 更改为 Keys.D1 表明该示例在其他方面有效。

如果有线索,RichTextBox 响应 Ctrl+Shift+0 的默认行为是更改字体。我去寻找提到这是内置快捷方式的文档,但我没有找到任何东西(也许我没有使用正确的搜索词)。

我应该如何检测 Ctrl+Shift+0 以便我可以抑制默认行为并编写自己的行为?

【问题讨论】:

  • 您是否尝试过在表单的 PreviewKeyDown 事件中处理按键?
  • 对你来说是个悲伤的消息,我什至在application-level 上尝试过PreFilterMessage 和Control + Shift + D0 似乎是blocked 但它的效果仍然适用,很难改变这种行为,但是您可以执行自己的代码,但几乎不会抑制默认行为。
  • PreviewKeyDown 似乎产生了相同的结果。

标签: c# winforms keyboard richtextbox shortcuts


【解决方案1】:

经过一些尝试,我发现了如何真正抑制Ctrl + Shift + D0。然而,新的问题更加棘手,Ctrl + Shift + D0 被抑制好了,但是在释放Ctrl and Shift 时生成了beep sound,这太烦人了,因为你说你想要覆盖这些组合键而不是丢弃它。所以不应该生成beep sound。

在搜索了很多之后,希望有一些style 应用于RichTextBox 以防止beep sound 或一些消息被丢弃,导致抑制beep sound,但没有任何这样的事情。我几乎感到失望,并打算让您的问题永远无人回答。我不想添加只是部分解决了您的问题的答案。但是幸运的是,我尝试了 sending some key 而不是丢弃的 0 key 到 consume 的键组合并使 keys combination valid 因此不会生成 beep sound。这是给你的完整代码,注意我们必须在这里使用一些global low-level keyboard hook,正如我所说的应用程序级消息过滤器也无济于事:

    [DllImport("user32")]
    private static extern IntPtr SetWindowsHookEx(int hookType, KeyboardLowLevelProc proc, IntPtr moduleHandle, int threadID);
    [DllImport("user32")]
    private static extern int UnhookWindowsHookEx(IntPtr hHook);
    [DllImport("user32")]
    private static extern IntPtr CallNextHookEx(IntPtr hHook, int nCode, IntPtr wParam, IntPtr lParam);
    [DllImport("kernel32")]
    private static extern IntPtr GetModuleHandle(string moduleName);

    public struct KBDLLHOOKSTRUCT
    {
        public Keys key;
        public int scanCode;
        public int flags;
        public int time;
        public IntPtr extra;
    }
    public delegate IntPtr KeyboardLowLevelProc(int hCode, IntPtr wParam, IntPtr lParam);
    public IntPtr KeyboardLowLevelCallback(int nCode, IntPtr wParam, IntPtr lParam)
    {
        if (nCode >= 0) {
            KBDLLHOOKSTRUCT kbd = (KBDLLHOOKSTRUCT)Marshal.PtrToStructure(lParam, typeof(KBDLLHOOKSTRUCT));
            if (kbd.key == Keys.D0 && blockD0) {                
                if(ModifierKeys == (Keys.Control | Keys.Shift)) {
                    SendKeys.Send("{ESC}");
                    //Add custom code as the response to Ctrl + Shift + D0 here
                    //....
                }
                return new IntPtr(1);//Discard the default behavior
            }
        }
        return CallNextHookEx(hHook, nCode, wParam, lParam);
    }
    bool blockD0;
    KeyboardLowLevelProc proc; //this should be declared in the form scope
    IntPtr hHook;
    //your Form1 constructor
    public Form1(){
       InitializeComponent();
       //Get current module Handle
       IntPtr currentModuleHandle = GetModuleHandle(System.Diagnostics.Process.GetCurrentProcess().MainModule.ModuleName);
       //Set the keyboard hook
       hHook = SetWindowsHookEx(13, proc, currentModuleHandle, 0);//WH_KEYBOARD_LL = 13
       //register these Key events for your richTextBox1 
       richTextBox1.KeyDown += (s, e) => {
            if(e.KeyCode != Keys.D0) blockD0 = true;
       };
       richTextBox1.KeyUp += (s, e) => {
            if (ModifierKeys == Keys.None) blockD0 = false;
       };
       //Unhook keyboard when form is closed
       FormClosed += (s,e) => {
          if (hHook != IntPtr.Zero) {
              UnhookWindowsHookEx(hHook);
              hHook = IntPtr.Zero;
          }
       }
    }

稍微解释一下:我不明白为什么我们必须在这里使用Global Low-level Keyboard hook,我猜当按下组合键Ctrl + Shift + D0时,可能会有一些关键消息被克隆并分派到另一个线程,这就是为什么当前线程中的所有手动拦截都无法拦截或覆盖Ctrl + Shift + D0,但是global low-level keyboard hook可以处理key messages 在当前模块的所有线程中,它可以拦截任何关键消息。

我提到了beep sound的问题,如果你想体验一下,把SendKeys.Send("{ESC}");去掉就行了,其实你可以试试其他的键,比如1,2,...组合键Ctrl + Shift + ... 有效并有助于避免任何beep sound。

更新

上面的解决方案工作正常,这是最好的解决方案,Ctrl + Shift + D0 应该被彻底彻底地丢弃。但是它有点长(如您所见)。我发现当您按下Ctrl + Shift + D0 时,会发送消息WM_INPUTLANGCHANGEREQUEST,此消息会导致您不想要的行为。所以我们可以尝试另一种解决方案,使用PreProcessMessage,您仍然可以捕获组合Ctrl + Shift + D0,但您不能丢弃它(因为它已分派到另一个线程),这意味着您可以在那里添加自己的代码,而不是丢弃Ctrl + Shift + D0,我们可以通过丢弃消息WM_INPUTLANGCHANGEREQUEST 来丢弃它导致的效果/行为。我们有以下代码:

//Create a custom RichTextBox class
public class CustomRichTextBox : RichTextBox {
   protected override void WndProc(ref Message m){
     if(m.Msg == 0x50) return; //WM_INPUTLANGCHANGEREQUEST = 0x50
     base.WndProc(ref m);
   }
   public override bool PreProcessMessage(ref Message msg) {            
        if (msg.Msg == 0x100)//WM_KEYDOWN = 0x100
        {
           Keys keyData = (Keys)msg.WParam | ModifierKeys;
           if(keyData == (Keys.Control | Keys.Shift | Keys.D0)){
             //your own code goes here...
           }
        }
        return base.PreProcessMessage(ref msg);
   }
}

您可以看到第二种方法要短得多,但是正如我所说,它实际上并没有抑制Ctrl + Shift + D0,它只是抑制了由消息WM_INPUTLANGCHANGEREQUEST引起的默认行为.我想这足以解决您的问题。

【讨论】:

  • 感谢您的努力。我没想过要尝试的一件事是 Spy++ 或某种检查生成的消息的方法。好决定。我确实更喜欢更简单的方法,并且可能会使用它。但是,您尝试过keyData == (Keys.Control | Keys.Shift | Keys.D0) 吗?对我来说,就像我在问题中所说的那样, keyData 实际上从未包含这些值。但是,也许将我的替换行为简单地放在检测到 WM_INPUTLANGCHANGEREQUEST 的位置和时间是安全的。
  • @CraigSilver 当然你在MouseDown 或PreProcessMessage 中找到的keyData 不会是这样,但是keyData 只是一个分配给(Keys)msg.WParam | ModifierKeys; 的变量,所以它的值可以是(Keys.Control | Keys.Shift | Keys.D0),ModifierKeys是这里的诀窍,它的值可以和实际的keyData一起使用。
猜你喜欢
  • 1970-01-01
  • 2014-11-07
  • 1970-01-01
  • 2012-03-18
  • 1970-01-01
  • 1970-01-01
  • 2014-05-17
  • 1970-01-01
  • 2012-11-24
相关资源
最近更新 更多