如何为WinForms RichTextBox覆盖Ctrl + Shift + 0(零)? [英] How do I override Ctrl+Shift+0 (zero) for WinForms RichTextBox?

查看:68
本文介绍了如何为WinForms RichTextBox覆盖Ctrl + Shift + 0(零)?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

For my subclassed RichTextBox class, I can capture, suppress the default behaviour of and re-purpose Ctrl+Shift+#, as long as # is between 1 and 9. For Ctrl+Shift+0, I can't. I have experimented with ProcessCmdKey in the form class and onKeyDown and PreProcessMessage in the control class.

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

Here is sample code meant for the control class that should suppress Ctrl+Shift+0 but does not:

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 则表明该示例可以正常工作.

However, changing Keys.D0 to Keys.D1 shows that the sample otherwise works.

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

If it's a clue, the default behaviour of the RichTextBox, in response to Ctrl+Shift+0, is to change the font. I went hunting for documentation that mentions this as a built-in shortcut but I didn't find anything (maybe I'm not using the correct search terms).

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

How should I detect Ctrl+Shift+0 so that I can suppress the default behaviour and write my own?

推荐答案

经过几次尝试,我发现了如何真正抑制 Ctrl + Shift + D0 .但是,新问题更加棘手,将 Ctrl + Shift + D0 压制为OK,但是释放 Ctrl和Shift 时会产生<哔>提示音,这很烦人,因为您说过要覆盖这些键组合而不是放弃.因此,不应产生哔声.

After some tries, I've found out how to actually suppress the Ctrl + Shift + D0. However the new problem was even tougher, the Ctrl + Shift + D0 was suppressed OK but the beep sound was generated when releasing the Ctrl and Shift, that's so annoying because you said you wanted to override these keys combination not discard it. So the beep sound should not be generated.

经过大量搜索后,可以将某些 style 应用于 RichTextBox ,以防止 beep sound 或某些消息被丢弃,从而导致压制蜂鸣声,但是没有任何这种东西.我几乎感到失望,并打算让您的问题永远未回答.我不想添加仅能部分解决您的问题的答案.但是幸运的是,我尝试使用发送一些键而不是丢弃的 0键消费组合键并制成 keys组合 有效,因此不会产生哔声.这是为您准备的全部代码,请注意,在这里我们必须使用一些 global低级键盘钩子,因为我说过应用程序级消息过滤器也无济于事:

After searching much with a hope there was some style to apply to RichTextBox to prevent the beep sound or some message to discard resulting in suppressing the beep sound but there are not any such things. I were almost disappointed and intended to let your question unanswered forever. I didn't want to add the answer which just partially solved your problem. However it was luckily that I tried sending some key instead of the discarded 0 key to consume the keys combination and make the keys combination valid so no beep sound would be generated. Here is the entire code for you, note that we have to use some global low-level keyboard hook here, as I said the Application-level message filter also couldn't help:

    [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 ,但是全局低级键盘钩子可以处理中的关键消息>当前模块的所有线程,它可以拦截任何关键消息.

A little about the explanation: I do not understand exactly why we have to use Global Low-level Keyboard hook here, I guess that when the keys combination Ctrl + Shift + D0 is pressed, there may be some key message cloned and dispatched to another thread, that's why all the manual interceptions in the current thread can't intercept or override the Ctrl + Shift + D0, however the global low-level keyboard hook can handle the key messages in all threads of the current module and it can intercept any key messages.

我提到过哔声问题,如果您想体验一下,只需删除 SendKeys.Send("{ESC}"); ,实际上,您可以尝试其他一些键,例如 1 2 ,...,它们也可以使组合键 Ctrl + Shift + ... 有效并提供帮助避免发出任何哔声.

I mentioned about the beep sound problem, if you want to experience it, just remove the SendKeys.Send("{ESC}");, in fact you can try some other keys like 1, 2, ... they also make the keys combination Ctrl + Shift + ... valid and help avoid any beep sound.

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

The solution above works OK, it's the best solution with the point that the Ctrl + Shift + D0 should be discarded cleanly and totally. However it's a little long (as you can see). I've found that when you press the Ctrl + Shift + D0, the message WM_INPUTLANGCHANGEREQUEST is sent, this message causes the behavior which you don't want. So we can try another solution, with PreProcessMessage you can still catch the combination Ctrl + Shift + D0 but you just can't discard it (because it's dispatched to another thread), that means you can add your own code there, instead of discarding the Ctrl + Shift + D0, we can discard the effect/behavior it causes instead by discarding the message WM_INPUTLANGCHANGEREQUEST. We have the following code:

//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 引起的默认行为.我想这足以解决您的问题.

You can see that the second approach is much shorter, however as I said, it doesn't actually suppress the Ctrl + Shift + D0, it just suppresses the default behavior caused by the message WM_INPUTLANGCHANGEREQUEST. I guess it's enough to solve your problem.

这篇关于如何为WinForms RichTextBox覆盖Ctrl + Shift + 0(零)?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆