.net 修改RichTextBox中的默认选项卡大小

.net 修改RichTextBox中的默认选项卡大小,.net,winforms,tabs,richtextbox,.net,Winforms,Tabs,Richtextbox,有没有办法更改.NET RichTextBox中的默认选项卡大小? 目前,它似乎被设置为相当于8个空间,这对我来说有点大 编辑:为了澄清,我想将“\t”显示的全局默认值设置为控件的4个空格。据我所知,SelectionTabs属性要求您首先选择所有文本,然后通过数组选择选项卡宽度。如果有必要,我会这样做,但如果可能的话,我宁愿只更改一次全局默认值,这样我就不必每次都这样做。您可以通过设置属性来设置它 private void Form1_Load(object sender, EventArgs

有没有办法更改.NET RichTextBox中的默认选项卡大小? 目前,它似乎被设置为相当于8个空间,这对我来说有点大


编辑:为了澄清,我想将“\t”显示的全局默认值设置为控件的4个空格。据我所知,SelectionTabs属性要求您首先选择所有文本,然后通过数组选择选项卡宽度。如果有必要,我会这样做,但如果可能的话,我宁愿只更改一次全局默认值,这样我就不必每次都这样做。

您可以通过设置属性来设置它

private void Form1_Load(object sender, EventArgs e)
{
    richTextBox1.SelectionTabs = new int[] { 100, 200, 300, 400 };
}
更新:
顺序很重要

如果在初始化控件文本之前设置选项卡,则不必在设置选项卡之前选择文本

例如,在上述代码中,这将使文本保留原来的8个空格制表位:

richTextBox1.Text = "\t1\t2\t3\t4";
richTextBox1.SelectionTabs = new int[] { 100, 200, 300, 400 };
但这将使用新的:

richTextBox1.SelectionTabs = new int[] { 100, 200, 300, 400 };
richTextBox1.Text = "\t1\t2\t3\t4";

Winforms没有一个属性来设置带有单个数字的RichTexBox的默认选项卡大小,但是如果您准备深入研究RichTexBox的Rtf并进行修改,则可以使用一个名为“\deftab”的设置。之后的数字表示推数(1点=1/72英寸=20推)。标准选项卡大小为720 twips的结果Rtf可能类似于:

{\rtf1\ansi\ansicpg1252\deff0\deflang2057\deftab720{\fonttbl{\f0\fnil\fcharset0 Microsoft Sans Serif;}}
\viewkind4\uc1\pard\f0\fs41
1\tab 2\tab 3\tab 4\tab 5\par
}
如果需要将twip转换为像素,请使用以下代码:


如果您有一个RTF框,它只用于显示(只读)固定音高的文本,最简单的方法就是不要乱用制表符。简单地用空格替换它们


如果您希望用户可以输入一些内容并使用该Tab键前进,您还可以通过覆盖
OnKeyDown()
并打印空格来捕获Tab键。

奇怪的是,一直以来都没有人提出这种方法)

我们可以从
RichTextBox
继承并重写CmdKey处理程序()
如下所示:

public class TabRichTextBox : RichTextBox
{
    [Browsable(true), Category("Settings")]
    public int TabSize { get; set; } = 4;

    protected override bool ProcessCmdKey(ref Message Msg, Keys KeyData)
    {
            
        const int WM_KEYDOWN = 0x100;       // https://docs.microsoft.com/en-us/windows/desktop/inputdev/wm-keydown
        const int WM_SYSKEYDOWN = 0x104;    // https://docs.microsoft.com/en-us/windows/desktop/inputdev/wm-syskeydown
        // Tab has been pressed
        if ((Msg.Msg == WM_KEYDOWN || Msg.Msg == WM_SYSKEYDOWN) && KeyData.HasFlag(Keys.Tab))
        {
            // Let's create a string of spaces, which length == TabSize
            // And then assign it to the current position
            SelectedText += new string(' ', TabSize);

            // Tab processed
            return true;
        }
        return base.ProcessCmdKey(ref Msg, KeyData);
    }
}

现在,当您按Tab键时,将在控制区域中插入指定数量的空格,而不是
\t

我将该类与单间距字体一起使用;它将所有选项卡替换为空格

您只需根据需要设置以下设计器属性:

  • AcceptsTab=True TabSize
  • convertTabtospace=True
  • TabSize=4
PS:正如@ToolmakerSteve所指出的,这里的制表符大小逻辑显然非常简单:它只是用4个空格替换制表符,这只适用于每行开头的制表符。如果需要改进选项卡处理,只需扩展逻辑即可

代码

using System.ComponentModel;
using System.Windows.Forms;

namespace MyNamespace
{
    public partial class MyRichTextBox : RichTextBox
    {
        public MyRichTextBox() : base() =>
            KeyDown += new KeyEventHandler(RichTextBox_KeyDown);

        [Browsable(true), Category("Settings"), Description("Convert all tabs into spaces."), EditorBrowsable(EditorBrowsableState.Always), DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
        public bool ConvertTabToSpaces { get; set; } = false;

        [Browsable(true), Category("Settings"), Description("The number os spaces used for replacing a tab character."), EditorBrowsable(EditorBrowsableState.Always), DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
        public int TabSize { get; set; } = 4;

        [Browsable(true), Category("Settings"), Description("The text associated with the control."), Bindable(true), EditorBrowsable(EditorBrowsableState.Always), DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
        public new string Text
        {
            get => base.Text;
            set => base.Text = ConvertTabToSpaces ? value.Replace("\t", new string(' ', TabSize)) : value;
        }

        protected override bool ProcessCmdKey(ref Message Msg, Keys KeyData)
        {
            const int WM_KEYDOWN = 0x100; // https://docs.microsoft.com/en-us/windows/desktop/inputdev/wm-keydown
            const int WM_SYSKEYDOWN = 0x104; // https://docs.microsoft.com/en-us/windows/desktop/inputdev/wm-syskeydown

            if (ConvertTabToSpaces && KeyData == Keys.Tab && (Msg.Msg == WM_KEYDOWN || Msg.Msg == WM_SYSKEYDOWN))
            {
                SelectedText += new string(' ', TabSize);
                return true;
            }
            return base.ProcessCmdKey(ref Msg, KeyData);
        }

        public new void AppendText(string text)
        {
            if (ConvertTabToSpaces)
                text = text.Replace("\t", new string(' ', TabSize));
            base.AppendText(text);
        }

        private void RichTextBox_KeyDown(object sender, KeyEventArgs e)
        {
            if ((e.Shift && e.KeyCode == Keys.Insert) || (e.Control && e.KeyCode == Keys.V))
            {
                SuspendLayout();
                int start = SelectionStart;
                string end = Text.Substring(start);
                Text = Text.Substring(0, start);
                Text += (string)Clipboard.GetData("Text") + end;
                SelectionStart = TextLength - end.Length;
                ResumeLayout();
                e.Handled = true;
            }
        }

    } // class
} // namespace

添加以下内容可能会很有用:这些值是以像素为单位而不是以字符为单位的制表位。上提到了,但似乎有点违反直觉。关于如何自动计算给定单间距字体的制表位,以避免像上面那样硬编码制表位,有什么想法吗?除非您有14磅的空格、粗体空格或其他格式的空格。您是对的。我忘了提到,这主要对使用“Courier New”和只有一种字体大小的代码编辑器是有意义的。在“Courier New”中,粗体空格的宽度与普通空格的宽度相同。但是,如果您使用可变间距字体或不同的字体大小,用空格替换制表符不是一个好主意。制表符用于表示距离。如果最终用户使用的是单间距字体,那么您的“空格”只能起到一半的作用。这非常有效。一个小问题。我必须将KeyData.HasFlag(Keys.Tab)更改为KeyData==Keys.Tab这不是选项卡的工作方式。制表符不等于给定数量的空格。相反,制表符前进到下一个制表位。即使在单间距字体中,假设每8个字符有一个制表位。键入3个字符,然后键入一个制表符:将前进5个空格。同样重要的是,如果它是一个可编辑的框,用户返回并添加另外两个字符,那么现在该选项卡将只有三个空格(8-(3+2)=3)。在这两种情况下,这个提议都被打破了。@ToolmakerSteve,哦,真的吗?迷人的!这个答案是CmdKey处理的一个例子,因此实现尽可能简单(甚至没有可用的
Shift+Tab
)。你不喜欢这种行为吗?你当然不知道!这是不完整的。继续,使用CmdKey处理知识实现您想要的一个(基于意见,因为,例如,我对为空间用户实现第二个案例的IDE/文本编辑器知之甚少)。就那样simple@ToolmakerSteve不,没关系。正如我之前所说的,这是基于观点的,所以这个问题可能会引发空间对抗制表符holywars。很明显,你是标签的拥护者,也就是“你不能用空格代替标签”。你是你,没有人评判这是什么进步?无论如何,这不是选项卡的工作方式。制表符不等于给定数量的空格。相反,制表符前进到下一个制表位。即使在单间距字体中,假设每8个字符有一个制表位。键入3个字符,然后键入一个制表符:将前进5个空格。同样重要的是,如果它是一个可编辑的框,用户返回并添加另外两个字符,那么现在该选项卡将只有三个空格(8-(3+2)=3)。这个建议在这两种情况下都是无效的。@ToolmakerSteve:我简化了这个类中的制表符大小逻辑,因为我使用RichTextBox主要是为了日志目的,制表符通常位于每个字符串的开头,所以我决定保持简单。如果您需要支持诸如MSWord之类的选项卡行为,您是否需要在某个地方保留RichTextBox中显示的文本的额外副本,并在每次插入的文本影响制表时更新整个文本。这个逻辑要复杂得多,出于明显的性能原因,我没有实现它,因为我的用例是构造一个日志窗口。@ToolmakerSteve:不过,我想这段代码至少可以让您了解如何实现更复杂的日志窗口
using System.ComponentModel;
using System.Windows.Forms;

namespace MyNamespace
{
    public partial class MyRichTextBox : RichTextBox
    {
        public MyRichTextBox() : base() =>
            KeyDown += new KeyEventHandler(RichTextBox_KeyDown);

        [Browsable(true), Category("Settings"), Description("Convert all tabs into spaces."), EditorBrowsable(EditorBrowsableState.Always), DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
        public bool ConvertTabToSpaces { get; set; } = false;

        [Browsable(true), Category("Settings"), Description("The number os spaces used for replacing a tab character."), EditorBrowsable(EditorBrowsableState.Always), DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
        public int TabSize { get; set; } = 4;

        [Browsable(true), Category("Settings"), Description("The text associated with the control."), Bindable(true), EditorBrowsable(EditorBrowsableState.Always), DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
        public new string Text
        {
            get => base.Text;
            set => base.Text = ConvertTabToSpaces ? value.Replace("\t", new string(' ', TabSize)) : value;
        }

        protected override bool ProcessCmdKey(ref Message Msg, Keys KeyData)
        {
            const int WM_KEYDOWN = 0x100; // https://docs.microsoft.com/en-us/windows/desktop/inputdev/wm-keydown
            const int WM_SYSKEYDOWN = 0x104; // https://docs.microsoft.com/en-us/windows/desktop/inputdev/wm-syskeydown

            if (ConvertTabToSpaces && KeyData == Keys.Tab && (Msg.Msg == WM_KEYDOWN || Msg.Msg == WM_SYSKEYDOWN))
            {
                SelectedText += new string(' ', TabSize);
                return true;
            }
            return base.ProcessCmdKey(ref Msg, KeyData);
        }

        public new void AppendText(string text)
        {
            if (ConvertTabToSpaces)
                text = text.Replace("\t", new string(' ', TabSize));
            base.AppendText(text);
        }

        private void RichTextBox_KeyDown(object sender, KeyEventArgs e)
        {
            if ((e.Shift && e.KeyCode == Keys.Insert) || (e.Control && e.KeyCode == Keys.V))
            {
                SuspendLayout();
                int start = SelectionStart;
                string end = Text.Substring(start);
                Text = Text.Substring(0, start);
                Text += (string)Clipboard.GetData("Text") + end;
                SelectionStart = TextLength - end.Length;
                ResumeLayout();
                e.Handled = true;
            }
        }

    } // class
} // namespace