Подавить разрыв строки в RichTextBox в определенных позициях - PullRequest
4 голосов
/ 15 июня 2011

Мне нужно подавить некоторые разрывы строк в RichTextBox.

Например, рассмотрим d6+. Не должно быть разрыва строки между 6 и +. В основном я ищу что-то вроде <nobr> в HTML.

До сих пор я возился со вставкой \ u + FEFF и т. Д. (Это работало на некоторых машинах, но некоторые показывали вертикальные линии, возможно, проблема со шрифтом, хотя стандартный шрифт Windows). Я также пытался манипулировать rtf напрямую, то есть box.rtf = ..., добавив туда \zwnbo, но я, кажется, никогда не понял это правильно.

Помощь очень ценится.

Ответы [ 4 ]

4 голосов
/ 21 июня 2011

Да, вы можете использовать все API RichText с вашим элементом управления RichTextBox.

Вам может быть интересно взглянуть на следующие сайты:

http://www.pinvoke.net/default.aspx/user32.sendmessage - как отправлять сообщения в окна с помощью p / invoke.

Вы можете использовать свойство Handle RichTextBox, чтобы получить дескриптор окна этого элемента управления, а затем отправлять ему сообщения .

Также посмотрите на эти включаемые файлы, поставляемые с SDK от Microsoft, которые не будут использоваться непосредственно в C #, но эти файлы содержат все константы, которые вам могут понадобиться, такие как WB_ISDELIMITER, WB_CLASSIFY и другие.

  • winuser.h
  • richedit.h

В следующем примере I продемонстрировать, как использовать API при условии.

EDIT:

Этот новый пример имеет код, помеченный как небезопасный, но он лучше, потому что он не страдает от проблемы односимвольной строки, поскольку я могу иметь параметр 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;
        }
    }
}

Это всего лишь черновик, поэтому вам, возможно, придется улучшить его, чтобы достичь своих целей.

Поместите элемент управления в форму окна и запустите.

Измените размер окна и посмотрите, хотите ли вы это сделать!

Вам придется искать границы слов ... Мне пока не удалось заставить его работать.

2 голосов
/ 18 июня 2011

Я не уверен, но если 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

Надеюсь, это поможет.

0 голосов
/ 22 июня 2011

Вот мое решение (на основе @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;
        }
    }
}

Обратите внимание, что вам нужно сохранить метод делегата, иначе он будет аварийно завершать работу при сборе с сборщика мусора (что было затруднительно при отладке).

По сути, этот подкласс разбивается только на пробелы, что на данный момент достаточно для моих нужд.

0 голосов
/ 21 июня 2011

Я быстро попробовал это, похоже, работает:

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);
...