How to remove annoying BEEP with RichTextBox

前端 未结 4 1644
星月不相逢
星月不相逢 2021-01-13 19:28

I placed a RichTextBox control on a new form and launched the project. So the RichTextBox.Text = \"\";

Each time I press Up or Down keys I heard the annoying BEEP so

相关标签:
4条回答
  • 2021-01-13 20:09

    first we need send EM_GETOLEINTERFACE message to rich edit window - this is Retrieves an IRichEditOle object that a client can use to access a rich edit control's Component Object Model (COM) functionality.

    then for retrieve an ITextServices pointer, call QueryInterface on the private IUnknown pointer returned by EM_GETOLEINTERFACE.

    here exist interesting point - the IID_ITextServices not well known but need get in runtime from Msftedit.dll

    from About Windowless Rich Edit Controls

    Msftedit.dll exports an interface identifier (IID) called IID_ITextServices that you can use to query the IUnknown pointer for the ITextServices interface.

    after we got ITextServices pointer - we simply can call OnTxPropertyBitsChange(TXTBIT_ALLOWBEEP, 0)

    code example:

        if (HMODULE hmodRichEdit = LoadLibrary(L"Msftedit.dll"))
        {
            // create richedit window
            if (HWND hwndRich = CreateWindowExW(0, MSFTEDIT_CLASS, ...))
            {
                if (IID* pIID_ITS = (IID*) GetProcAddress(hmodRichEdit, "IID_ITextServices"))
                {
                    IUnknown* pUnk;
                    if (SendMessageW(hwndRich, EM_GETOLEINTERFACE, 0, (LPARAM)&pUnk))
                    {
                        ITextServices* pTxtSrv;
                        HRESULT hr = pUnk->QueryInterface(*pIID_ITS, (void**)&pTxtSrv);
                        pUnk->Release();
                        if (0 <= hr)
                        {
                            pTxtSrv->OnTxPropertyBitsChange(TXTBIT_ALLOWBEEP, 0);
                            pTxtSrv->Release();
                        }
                    }
                }
            }
        }
    
    0 讨论(0)
  • 2021-01-13 20:14

    Well, you can try suppressing the key only if the caret is on the first line and the key is up, or on the last line and the key is down or in position 0 and the key is left, etc.

    That said, this is how most textboxes behave, and the sound is set in your operating system preferences. You'd get the same beep in, say, Wordpad or Outlook, if you try to move the caret beyond the limit of a textbox.

    0 讨论(0)
  • 2021-01-13 20:14

    This code below should stop the beeping sound, and works with wrapped and unwrapped text:

    private void richTextBox1_KeyDown(object sender, KeyEventArgs e)
    {
        if (
            richTextBox1.GetLineFromCharIndex(richTextBox1.SelectionStart) == 0 && e.KeyData == Keys.Up ||
            richTextBox1.GetLineFromCharIndex(richTextBox1.SelectionStart) == richTextBox1.GetLineFromCharIndex(richTextBox1.TextLength) && e.KeyData == Keys.Down ||
            richTextBox1.SelectionStart == richTextBox1.TextLength && e.KeyData == Keys.Right ||
            richTextBox1.SelectionStart == 0 && e.KeyData == Keys.Left
        ) e.Handled = true;
    }
    
    0 讨论(0)
  • 2021-01-13 20:14

    The following is a .Net implementation of using the ITextServices interface method described in @RbMm's answer. If you find this useful, please consider upvoting that answer.

    This implementation exposes two extension methods (EnableBeep and DisableBeep) for the Winform RichTextBox. Both a C# and Vb.Net version are provided.

    As we are only interested in using the OnTxPropertyBitsChange function defined in the ITextServices interface, an abbreviated implementation of the interface is used similar to those created when "Embed Interop Types" is used for a COM reference. If you have installed the Windows SDK, you should find the "TextServ.h" file at a path similar to "C:\Program Files (x86)\Windows Kits\10\Include\10.0.18362.0\um\TextServ.h". This file defines the full ITextServices class interface.

    C# version

    using System;
    using System.Runtime.InteropServices;
    using System.Windows.Forms;
    //----
    public static class RTBBeepExtensions
    {
        public static void EnableBeep(this RichTextBox rtb)
        {
            SetBeepInternal(rtb, true);
        }
    
        public static void DisableBeep(this RichTextBox rtb)
        {
            SetBeepInternal(rtb, false);
        }
    
        private static void SetBeepInternal(RichTextBox rtb, bool beepOn)
        {
            const Int32 WM_USER = 0x400;
            const Int32 EM_GETOLEINTERFACE = WM_USER + 60;
            const Int32 COMFalse = 0;
            const Int32 COMTrue = ~COMFalse; // -1
    
            ITextServices textServices = null;
            // retrieve the rtb's OLEINTERFACE using the Interop Marshaller to cast it as an ITextServices
            // The control calls the AddRef method for the object before returning, so the calling application must call the Release method when it is done with the object.
            SendMessage(new HandleRef(rtb, rtb.Handle), EM_GETOLEINTERFACE, IntPtr.Zero, ref textServices);
            textServices.OnTxPropertyBitsChange(TXTBIT.ALLOWBEEP, beepOn ? COMTrue : COMFalse));
            Marshal.ReleaseComObject(textServices);
        }
    
        [DllImport("user32.dll", CharSet = CharSet.Auto)]
        private extern static IntPtr SendMessage(HandleRef hWnd, Int32 msg, IntPtr wParam, ref ITextServices lParam);
    
        #region ITextServices // From TextServ.h
        [ComImport(), Guid("8d33f740-cf58-11ce-a89d-00aa006cadc5"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
        private interface ITextServices
        {
            //see: Slots in the V-table
            //     https://docs.microsoft.com/en-us/dotnet/framework/unmanaged-api/metadata/imetadataemit-definemethod-method#slots-in-the-v-table
            void _VtblGap1_16();
            Int32 OnTxPropertyBitsChange(TXTBIT dwMask, Int32 dwBits);
        }
    
        private enum TXTBIT : uint
        {
            /// <summary>If TRUE, beeping is enabled.</summary>
            ALLOWBEEP = 0x800
        }
        #endregion
    
    }
    

    VB.Net version

    Imports System
    Imports System.Runtime.InteropServices
    Imports System.Runtime.CompilerServices
    Imports System.Windows.Forms
    ' -------
    Public Module RTBBeepExtensions
      <Extension()>
      Public Sub EnableBeep(rtb As RichTextBox)
        SetBeepInternal(rtb, True)
      End Sub
    
      <Extension()>
      Public Sub DisableBeep(rtb As RichTextBox)
        SetBeepInternal(rtb, False)
      End Sub
    
      Private Sub SetBeepInternal(rtb As RichTextBox, beepOn As Boolean)
        Const WM_USER As Int32 = &H400
        Const EM_GETOLEINTERFACE As Int32 = WM_USER + 60
        Const COMFalse As Int32 = 0
        Const COMTrue As Int32 = Not COMFalse ' -1
    
        Dim textServices As ITextServices = Nothing
        ' retrieve the rtb's OLEINTERFACE using the Interop Marshaller to cast it as an ITextServices
        ' The control calls the AddRef method for the object before returning, so the calling application must call the Release method when it is done with the object.
        SendMessage(New HandleRef(rtb, rtb.Handle), EM_GETOLEINTERFACE, IntPtr.Zero, textServices)
        textServices.OnTxPropertyBitsChange(TXTBIT.ALLOWBEEP, If(beepOn, COMTrue, COMFalse))
        Marshal.ReleaseComObject(textServices)
      End Sub
    
      <DllImport("user32.dll", CharSet:=CharSet.Auto)>
      Private Function SendMessage(ByVal hWnd As HandleRef, ByVal msg As Int32, ByVal wParam As IntPtr, ByRef lParam As ITextServices) As IntPtr
      End Function
    
    #Region "ITextServices" ' From TextServ.h
      <ComImport(), Guid("8d33f740-cf58-11ce-a89d-00aa006cadc5"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)>
      Private Interface ITextServices
        'see: Slots in the V-table
        '     https://docs.microsoft.com/en-us/dotnet/framework/unmanaged-api/metadata/imetadataemit-definemethod-method#slots-in-the-v-table
        Sub _VtblGap1_16()
        Function OnTxPropertyBitsChange(ByVal dwMask As TXTBIT, ByVal dwBits As Int32) As Int32
      End Interface
    
      Private Enum TXTBIT As UInt32
        ''' <summary>If TRUE, beeping is enabled.</summary>
        ALLOWBEEP = &H800
      End Enum
    #End Region
    
    End Module
    

    If you are unfamiliar with using extension methods, please refer to the following documentation links.

    Extension Methods (C# Programming Guide)

    Extension Methods (Visual Basic)

    0 讨论(0)
提交回复
热议问题