C# WinForms RichTextBox文本之间的垂直间距略有不同

qncylg1j  于 2023-02-24  发布在  C#
关注(0)|答案(2)|浏览(205)

我尝试通过一个包含两个RichTextBox的拆分视图向自制编辑器添加行号。左边的是行号,右边的是实际文本。然而,我看到行号逐渐与文本不同步。我不确定通过截图测量是否准确,但尽管使用相同的字体,行编号文本框中字符之间的垂直间距为7像素,而主编辑器窗格中字符之间的垂直间距为6像素。实际字符本身在两个视图中具有相同的高度。
我使用以下代码设置所有内容

public class RTLSourceView : SplitContainer
{
    public RTLSourceView(string filename)
    {
        DoubleBuffered = true;
        Dock = DockStyle.Fill;
        
        RtlRichTextBox rtb = new RtlRichTextBox();
        rtb.Text = System.IO.File.ReadAllText(filename);
        rtb.Dock = DockStyle.Fill;
        rtb.ForeColor = Color.White;
        rtb.BackColor = Color.Black;
        rtb.Dock = DockStyle.Fill;
        rtb.VScroll += richTextBox1_VScroll;
        Panel2.Controls.Add(rtb);

        RichTextBox line_numbers = new RichTextBox();
        line_numbers.Multiline = true;
        line_numbers.Dock = DockStyle.Fill;
        line_numbers.WordWrap = false;
        line_numbers.ReadOnly = true;
        line_numbers.ForeColor = Color.White;
        line_numbers.BackColor = Color.Black;
        line_numbers.SelectionLength = 0;
        line_numbers.SizeChanged += onSizeChange;
        line_numbers.Font = rtb.Font;
        line_numbers.ScrollBars = RichTextBoxScrollBars.None;
        Panel1.Controls.Add(line_numbers);

我将line_numbers.Font设置为rtb.Font,这在调试器中看起来确实没问题。我更像是SystemVerilog程序员而不是C#程序员,所以我可能遗漏了一些其他人都知道的明显设置,但我很好奇是什么原因使附加到同一拆分面板的具有相同字体的两个RichTextBox具有两个不同的字体间距

r7s23pms

r7s23pms1#

这可能会有点棘手,因为不仅仅是字体的问题,例如,行间距会起作用,因为rtf中的每个CR都将是一个新的/par

下面是计算字符位置并在Y坐标改变时生成新行标签的方案的第一遍。

public partial class RichTextBoxEx : UserControl
{
    const int 
        MAX_VISIBLE_LINES = 100,
        LINE_LABEL_HEIGHT = 20;
    public RichTextBoxEx()
    {
        InitializeComponent();
        for (int i = 0; i < MAX_VISIBLE_LINES; i++)
        {
            panel.Controls.Add(new Label
            {
                Name = $"label{i}",
                Text = "Placeholder",
                Location = new Point(0, i * LINE_LABEL_HEIGHT),
                TextAlign = ContentAlignment.MiddleLeft,
                Size = new Size(panel.Width, LINE_LABEL_HEIGHT),
                AutoSize = false,
            });
        }
        _labels = panel.Controls.OfType<Label>().ToArray();
    }
    private readonly Label[] _labels;
    protected override void OnCreateControl()
    {
        base.OnCreateControl();
        richTextBox.VScroll += recalc;
        richTextBox.TextChanged += recalc;
        richTextBox.Rtf = rtf;
    }

    private void recalc(object sender, EventArgs e)
    {
        int charIndex = richTextBox
        .GetLineFromCharIndex(
            richTextBox.GetCharIndexFromPosition(Point.Empty));

        int y, delta, cum = 0, prevY = 0;
        int labelIndex = 0;
        while(charIndex < richTextBox.Text.Length)
        {
            Point pos = richTextBox.GetPositionFromCharIndex(charIndex);
            y = pos.Y;
            if (!y.Equals(prevY))
            {
                delta = y - prevY;
                cum += delta;
                Debug.WriteLine($"{prevY} {y} {delta} {cum}");
                prevY = y;
                _labels[labelIndex].Top = cum;
                _labels[labelIndex].Visible = true;
                _labels[labelIndex].Text = $"{labelIndex + 1}";
                labelIndex++;
            }
            charIndex++;
        }
        while (labelIndex < MAX_VISIBLE_LINES)
        {
            _labels[labelIndex++].Visible = false;
        }
    }

    const string rtf =
@"{\rtf1\ansi\ansicpg1252\deff0\nouicompat\deflang1033{\fonttbl{\f0\fnil\fcharset0 Calibri;}}
{\*\generator Riched20 10.0.22621}\viewkind4\uc1 
\pard\sa200\sl276\slmult1\f0\fs22\lang9 Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed pellentesque, odio ac posuere vehicula, risus arcu cursus nunc, a varius nisi lectus fermentum quam. Maecenas dignissim metus sed felis sodales suscipit. Aliquam sagittis pharetra massa, eu hendrerit erat maximus non. Nulla facilisi. Quisque libero urna, vulputate ut maximus in, sodales vitae mi. \par
\b\fs40 Suspendisse\b0\fs22  \par
In est metus. Donec id turpis lorem. Curabitur id ipsum bibendum, fringilla eros vel, vulputate enim. Pellentesque nec sem a sem rhoncus ultrices. Quisque at purus a odio semper sagittis a at tellus. \par
}";
}
bhmjp9jg

bhmjp9jg2#

@IVSoftware有一个很好的答案,因为它考虑到每行的字体可能不同。
但是,如果整个文档的字体相同,则需要将事件分配给rtbFontChange事件,或者在对话框中设置字体时将字体分配给两个RTBox。
您的RTBox似乎在RTLSourceView(...)方法的作用域之外不可用,因此要访问它,您需要知道控件在面板中的位置(或循环通过Controls属性)。RtlRichTextBox可以在初始化时实现某些默认字体,在您代码中,有一个RtlRichTextBox用于rtb,有一个标准的RichTextBox用于line_numbers

public partial class Form1 : Form
    {
        //RichTextBox line_numbers;
        //RichTextBox rtb;
        public Form1()
        {
            InitializeComponent();

            DoubleBuffered = true;
            Dock = DockStyle.Fill;

            RichTextBox rtb = new RichTextBox();
            //rtb.Text = System.IO.File.ReadAllText(filename);
            rtb.Dock = DockStyle.Fill;
            rtb.ForeColor = Color.White;
            rtb.BackColor = Color.Black;
            rtb.Dock = DockStyle.Fill;
            rtb.TextChanged += Rtb_TextChanged;
            rtb.FontChanged += Rtb_FontChanged;
            splitContainer1.Panel2.Controls.Add(rtb);

            RichTextBox line_numbers = new RichTextBox();
            line_numbers.Multiline = true;
            line_numbers.Dock = DockStyle.Fill;
            line_numbers.WordWrap = false;
            line_numbers.ReadOnly = true;
            line_numbers.ForeColor = Color.White;
            line_numbers.BackColor = Color.Black;
            line_numbers.SelectionLength = 0;
            line_numbers.Font = rtb.Font;
            line_numbers.ScrollBars = RichTextBoxScrollBars.None;
            splitContainer1.Panel1.Controls.Add(line_numbers);
        }

        private void Rtb_FontChanged(object sender, EventArgs e)
        {
            if (sender is RichTextBox rtb)
            {
                if (splitContainer1.Panel1.Controls[0] is RichTextBox line_numbers)
                {
                    line_numbers.Font = rtb.Font;
                }
            }
        }

        private void Rtb_TextChanged(object sender, EventArgs e)
        {
            if (sender is RichTextBox rtb)
            {
                if (splitContainer1.Panel1.Controls[0] is RichTextBox line_numbers)
                {
                    line_numbers.Clear();
                    for (int i = 0; i < rtb.Lines.Length; i++)
                    {
                        line_numbers.Text += $"{i + 1}{Environment.NewLine}";
                    }
                }
            }
        }

        private void button1_Click(object sender, EventArgs e)
        {
            if (fontDialog1.ShowDialog() == DialogResult.OK)
            {
                if (splitContainer1.Panel2.Controls[0] is RichTextBox rtb)
                {
                    rtb.Font = fontDialog1.Font;
                }
            }
        }
    
    }

上面的代码只是一个表单,其中有一个按钮用于调用FontDialog.ShowDialog()和拆分面板。

相关问题