【问题标题】:Suppress line break in a RichTextBox at certain positions抑制 RichTextBox 中某些位置的换行符
【发布时间】:2011-09-15 15:07:35
【问题描述】:

我需要抑制 RichTextBox 中的一些换行符。

例如,考虑d6+6+ 之间不能有换行符。基本上我在 HTML 中寻找类似<nobr> 的东西。

到目前为止,我已经搞砸了插入 \u+FEFF 等(这在某些机器上有效,但有些显示垂直线,尽管 Windows 标准字体可能是字体问题)。我还尝试直接操作 rtf,即 box.rtf = ... 并在其中放置一些 \zwnbo,但我似乎从来没有做对。

非常感谢您的帮助。

【问题讨论】:

    标签: c# .net richtextbox rtf line-breaks


    【解决方案1】:

    我不确定,但如果 RichTextBox 实际上包装了一个 Windows 富编辑控件,那么阅读以下内容可能会带来一些运气:

    http://msdn.microsoft.com/en-us/library/hh270412%28v=vs.85%29.aspx

    或者更具体地说:

    http://msdn.microsoft.com/en-us/library/bb787877%28v=vs.85%29.aspx

    我希望这会有所帮助。

    【讨论】:

    • 我不确定这是否适用于.net
    • @Eiko 似乎接受的答案假设了我在这里的建议。但代码很难跨平台。
    • 我想现在,大约 10 年后,我会使用完全不同的技术。 :-)
    【解决方案2】:

    是的,您可以将所有 RichText API 与您的 RichTextBox 控件一起使用。

    您可能有兴趣看看以下网站:

    http://www.pinvoke.net/default.aspx/user32.sendmessage - 如何使用 p/invoke 向窗口发送消息。

    您可以使用RichTextBoxHandle 属性来获取该控件的窗口句柄,然后向它发送消息

    还请查看 Microsoft SDK 附带的这些包含文件,它们不会直接在 C# 中使用,但这些文件包含您可能必须使用的所有常量,例如 WB_ISDELIMITERWB_CLASSIFY 等。

    • winuser.h
    • richedit.h

    在下面的例子中,我 演示如何使用 API 提供。

    编辑:

    这个新样本的代码被标记为不安全,但它更好,因为它没有单个字符串的问题,因为我可以有一个char* 参数并对其进行操作。 旧样本如下:

    这是 C# 代码,不是 C++...要编译它,您必须转到项目选项并标记复选框以允许不安全的代码运行 .

    右键项目->属性(Alt+Enter)->构建->常规->允许不安全代码(必须勾选)

    using System;
    using System.Runtime.InteropServices;
    using System.Windows.Forms;
    
    namespace q6359774
    {
        class MyRichTextBox : RichTextBox
        {
            const int EM_SETWORDBREAKPROC = 0x00D0;
            const int EM_GETWORDBREAKPROC = 0x00D1;
    
            protected override void OnHandleCreated(EventArgs e)
            {
                base.OnHandleCreated(e);
                this.Text = "abcdefghijklmnopqrstuvwxyz-abcdefghijklmnopqrstuvwxyz";
                NewMethod();
            }
    
            unsafe private void NewMethod()
            {
                if (!this.DesignMode)
                    SendMessage(this.Handle, EM_SETWORDBREAKPROC, IntPtr.Zero, Marshal.GetFunctionPointerForDelegate(new EditWordBreakProc(MyEditWordBreakProc)));
            }
    
            [DllImport("User32.DLL")]
            public static extern IntPtr SendMessage(IntPtr hWnd, UInt32 Msg, IntPtr wParam, IntPtr lParam);
    
            unsafe delegate int EditWordBreakProc(char* lpch, int ichCurrent, int cch, int code);
    
            unsafe int MyEditWordBreakProc(char* lpch, int ichCurrent, int cch, int code)
            {
                const int WB_ISDELIMITER = 2;
                const int WB_CLASSIFY = 3;
                if (code == WB_ISDELIMITER)
                {
                    char ch = *lpch;
                    return ch == '-' ? 0 : 1;
                }
                else if (code == WB_CLASSIFY)
                {
                    char ch = *lpch;
                    var vResult = Char.GetUnicodeCategory(ch);
                    return (int)vResult;
                }
                else
                {
                    var lpch2 = lpch;
                    // in this case, we must find the begining of a word:
                    for (int it = ichCurrent; it < cch; it++)
                    {
                        char ch = *lpch2;
                        if (it + 1 < cch && lpch2[0] == '-' && lpch2[1] != '-')
                            return it;
                        if (lpch2[0] == '\0')
                            return 0;
                        lpch2++;
                    }
                }
    
                return 0;
            }
        }
    }
    

    旧示例代码

    该示例包含一个继承自 RichTextBox 的类,并使用 EM_SETWORDBREAKPROC 放置一个自定义处理程序。此类只会在超过“-”字符时准确地换行。不是之前,也不是之后。

    using System;
    using System.Runtime.InteropServices;
    using System.Windows.Forms;
    
    namespace q6359774
    {
        class MyRichTextBox : RichTextBox
        {
            const int EM_SETWORDBREAKPROC = 0x00D0;
            const int EM_GETWORDBREAKPROC = 0x00D1;
    
            protected override void OnHandleCreated(EventArgs e)
            {
                base.OnHandleCreated(e);
                this.Text = "abcdefghijklmnopqrstuvwxyz-abcdefghijklmnopqrstuvwxyz";
                if (!this.DesignMode)
                    SendMessage(this.Handle, EM_SETWORDBREAKPROC, IntPtr.Zero, Marshal.GetFunctionPointerForDelegate(new EditWordBreakProc(MyEditWordBreakProc)));
            }
    
            [DllImport("User32.DLL")]
            public static extern IntPtr SendMessage(IntPtr hWnd, UInt32 Msg, IntPtr wParam, IntPtr lParam);
    
            delegate int EditWordBreakProc(string lpch, int ichCurrent, int cch, int code);
    
            int MyEditWordBreakProc(string lpch, int ichCurrent, int cch, int code)
            {
                const int WB_ISDELIMITER = 2;
                const int WB_CLASSIFY = 3;
                if (code == WB_ISDELIMITER)
                {
                    if (lpch.Length == 0 || lpch == null) return 0;
                    char ch = lpch[ichCurrent];
                    return ch == '-' ? 0 : 1;
                }
                else if (code == WB_CLASSIFY)
                {
                    if (lpch.Length == 0 || lpch == null) return 0;
                    char ch = lpch[ichCurrent];
                    var vResult = Char.GetUnicodeCategory(ch);
                    return (int)vResult;
                }
                else
                {
                    if (lpch.Length == 0 || lpch == null) return 0;
                    for (int it = ichCurrent; it < lpch.Length; it++)
                    {
                        char ch = lpch[it];
                        if (ch != '-') return it;
                    }
                }
    
                return 0;
            }
        }
    }
    

    这只是一个草稿,所以你可能需要进一步改进它,这样你才能实现你的目标。

    将控件放入windows窗体中,然后运行。

    调整窗口大小,看看这是不是你想要做的!

    你将不得不寻找单词边界...我还没有设法让它工作。

    【讨论】:

    • 我目前正在试验它。但是,该方法在奇怪的时间/位置被调用。此外,lpch 只包含一个字符。也许这是一个编码的东西?
    • @Eiko:这是因为 EditWordBreakProc 的类型 lpch 是一个指向字符的指针(即 c++ 中的 char*),但编组器将其理解为指向单个字符串的指针这不是真的,它是一个指向 RichTextBox 文本缓冲区中间的字符的指针……这不是编码问题。
    • 也许我弄错了变量,但我已经设置了一个带有“aaaaaaaa bbbbbbbbb cccc”的文本框,并且使用 ichCurrent = 15 和 cch = 32 调用该方法(更长比字符串本身)。中断应该发生在 b 和 c 之间。
    • 我想我通过一些修改让它工作了。稍后我会将我的解决方案作为单独的答案提出并奖励您。
    • 刚刚发布了答案。需要进行一些修改,即存储委托方法以保留垃圾收集,并正确地左右行走以识别中断位置。不过,这是一个很好的起点。谢谢。
    【解决方案3】:

    我很快就试过了,它似乎有效:

    this.userControl.richTextBox1.LoadFile("C:\\test.rtf");
    this.userControl.richTextBox1.Rtf = this.userControl.richTextBox1.Rtf.Replace(@"\par", String.Empty);
    
    this.userControl.richTextBox1.SaveFile("C:\\test2.rtf", RichTextBoxStreamType.RichText);
    

    【讨论】:

    • 我不想删除所有段落。我需要抑制 某些 位置的换行符。
    • 是的,这是我所理解的,但我不知道您如何定义此类数据。如果您确切知道要更改的文本,只需在之前执行查找,然后从查找结果中的文本长度 + \par 替换,不是吗?
    • 我只是设置了一些 RichtTextBox 的文本。您甚至可以直接在框中输入它。没有明确的换行符。
    • 我不明白的是,当您说要在某些位置禁止换行时,“确定”是什么?
    • 见我的例子。我不能在 "d6+" 或 "e2-e4" 内中断。这就像保护某些区域不被破坏一样简单。不过,似乎只允许在空格处休息就可以了。
    【解决方案4】:

    这是我的解决方案(基于@Miguel Angelo,但稍作修改和纠正):

    using System;
    using System.Collections.Generic;
    using System.Text;
    using System.Windows.Forms;
    using System.Runtime.InteropServices;
    
    namespace MyNameSpace
    {
        public class SpaceBreakingRichTextBox : RichTextBox
        {
            const int EM_SETWORDBREAKPROC = 0x00D0;
            const int EM_GETWORDBREAKPROC = 0x00D1;
    
            protected override void OnHandleCreated(EventArgs e)
            {
                base.OnHandleCreated(e);
                AddDelegate();
            }
    
            [DllImport("User32.DLL")]
            public static extern IntPtr SendMessage(IntPtr hWnd, UInt32 Msg, IntPtr wParam, IntPtr lParam);
    
            unsafe delegate int EditWordBreakProc(char* lpch, int ichCurrent, int cch, int code);
            EditWordBreakProc myDelegate;
    
            unsafe private void AddDelegate()
            {
                if (!this.DesignMode)
                {
                    myDelegate = new EditWordBreakProc(MyEditWordBreakProc);
                    SendMessage(this.Handle, EM_SETWORDBREAKPROC, IntPtr.Zero, Marshal.GetFunctionPointerForDelegate(myDelegate));
                }
            }
    
            unsafe int MyEditWordBreakProc(char* lpch, int ichCurrent, int cch, int code)
            {
                const int WB_ISDELIMITER = 2;
                const int WB_CLASSIFY = 3;
                const int WB_MOVEWORDLEFT = 4;
                const int WB_MOVEWORDRIGHT = 5;
    
                const int WB_LEFTBREAK = 6;
                const int WB_RIGHTBREAK = 7;
    
                const int WB_LEFT = 0;
                const int WB_RIGHT = 1;
    
                if (code == WB_ISDELIMITER)
                {
                    char ch = *lpch;
                    return ch == ' ' ? 1 : 0;
                }
                else if (code == WB_CLASSIFY)
                {
                    char ch = *lpch;
                    var vResult = Char.GetUnicodeCategory(ch);
                    return (int)vResult;
                }
                else if (code == WB_LEFTBREAK)
                {
                    for (int it = ichCurrent; it >= 0; it--)
                    {
                        if (lpch[it] == ' '/* && lpch2[1] != ' '*/)
                        {
                            if (it > 0 && lpch[it - 1] != ' ')
                                return it;
                        }
                    }
                }
                else if (code == WB_RIGHT)
                {
                    for (int it = ichCurrent; ; it++)
                    {
                        if (lpch[it] != ' ')
                            return it;
                    }
                }
                else
                {
                     // There might be more cases to handle (see constants)
                }
                return 0;
            }
        }
    }
    

    请注意,您需要保留委托方法,否则它会在从垃圾收集器收集时崩溃(调试起来很痛苦)。

    基本上,这个子类只在空格处中断,这足以满足我目前的需求。

    【讨论】:

      猜你喜欢
      • 2014-12-23
      • 1970-01-01
      • 1970-01-01
      • 2020-08-14
      • 2014-09-26
      • 2017-01-29
      • 2011-01-27
      • 2017-06-23
      • 1970-01-01
      相关资源
      最近更新 更多