簡體   English   中英

如何使用 RichTextBox 去除煩人的 BEEP

[英]How to remove annoying BEEP with RichTextBox

我在一個新窗體上放置了一個 RichTextBox 控件並啟動了該項目。 所以 RichTextBox.Text = "";

每次我按向上或向下鍵時,我都會聽到煩人的 BEEP 聲音! 如何擺脫這個問題?

在 KeyDown 方法中使用“e.SuppressKeyPress = true”鎖定光標位置。

首先,我們需要將EM_GETOLEINTERFACE消息發送到富編輯窗口 - 這是檢索客戶端可以用來訪問富編輯控件的組件對象模型 (COM) 功能的 IRichEditOle 對象。

然后為了檢索ITextServices指針,在EM_GETOLEINTERFACE返回的私有IUnknown指針上調用QueryInterface

這里存在有趣的一點 - IID_ITextServices並不為人所知,但需要從Msftedit.dll進入運行時

來自關於無窗口豐富的編輯控件

Msftedit.dll導出一個名為IID_ITextServices的接口標識符 (IID),您可以使用它來查詢ITextServices接口的 IUnknown 指針。

在我們獲得ITextServices指針之后 - 我們可以簡單地調用OnTxPropertyBitsChange(TXTBIT_ALLOWBEEP, 0)

代碼示例:

    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();
                    }
                }
            }
        }
    }

以下是使用@RbMm 的回答中描述的 ITextServices 接口方法的 .Net 實現。 如果您覺得這很有用,請考慮對該答案進行投票。

此實現公開了EnableBeep RichTextBox 的兩個擴展方法( EnableBeepDisableBeep )。 提供了 C# 和 Vb.Net 版本。

由於我們只對使用 ITextServices 接口中定義的OnTxPropertyBitsChange函數感興趣,因此使用的接口的縮寫實現類似於在“嵌入互操作類型”用於 COM 引用時創建的那些實現。 如果您已經安裝了 Windows SDK,您應該在類似於“C:\\Program Files (x86)\\Windows Kits\\10\\Include\\10.0.18362.0\\um\\TextServ.h”的路徑下找到“TextServ.h”文件. 該文件定義了完整的 ITextServices 類接口。

C#版本

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版

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

如果您不熟悉使用擴展方法,請參考以下文檔鏈接。

擴展方法(C# 編程指南)

擴展方法 (Visual Basic)

下面的代碼應該停止嗶嗶聲,並處理包裝和未包裝的文本:

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 且鍵在左等時,您才可以嘗試抑制鍵。

也就是說,這是大多數文本框的行為方式,並且聲音是在您的操作系統首選項中設置的。 如果您嘗試將插入符號移出文本框的限制,您會在 Wordpad 或 Outlook 中得到同樣的提示音。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM