绘制多个没有重叠文本的字符串

Drawing multiple Strings with no overlapping Text

我想在 C# 中的一个矩形上打印多个不同的字符串。

我的问题是打印时字符串总是重叠。
我无法打印整个字符串,因为每一行都有另一种字体。

RectangleF[] rectangles = { 
    new RectangleF(650.8188976377953F, 57.48031496062993F, 275.59055118110234F, 334.6456692913386F) 
};

StringFormat stringFormat = new StringFormat();
stringFormat.Alignment = StringAlignment.Center;
stringFormat.LineAlignment = StringAlignment.Center;        

e.Graphics.DrawRectangles(blackPen, rectangles);
e.Graphics.DrawString("test1", headLine, black,rectangles[0],stringFormat);
e.Graphics.DrawString("test2", subLines, black, rectangles[0], stringFormat);

如何绘制它们,使它们不重叠并在不使用坐标的情况下将它们放置在我想要的任何位置?

您可以通过以下方式获取绘制的字符串大小;

Graphics.MeasureString("test1", StringFont)

然后您可以从返回的大小点扩展您的矩形目的地。

您的字符串重叠是因为您有一个绘图区域 (rectangles[0]),并且所有字符串都绘制在该区域中,没有任何关于它们位置的进一步指示。
由于 StringFormat.LineAlignment 设置为 StringAlignment.Center,所有文本都绘制在由 rectangles[0] 定义的区域的中心。

要将一系列字符串置于绘图区域内的中心位置,您可以将字符串序列加上分隔它们的 space 想象成一个矩形。
要将此矩形(绘图区域)居中放置在容器对象中,您可以将这些部分之间的关​​系定义为:

Drawing Area Height = (Text Height * Number of Strings) + (Interline * (Number of Strings - 1))

Blank Area Height = (Container Height - Drawing Area Height)

那么假想的Drawing Area在其Container中的居中位置为:

Container Top + (Blank Area Height / 2)

现在我们知道从哪里开始绘制文本行了。所有其他字符串位置都相对于已计算的绘图区域(因为它们都是此度量的一部分)。
这样,您就不需要手动将字符串放置在 Container 中。


为了定义不同的字符串及其相关属性(文本、字体、字体样式、文本高度、文本颜色),我使用了 class 对象,DrawingString

文本行由中间行分隔。
您可以修改此值并指定不同的行距:

float interline = 12.0f;

容器矩形内的文本位置 (rectangles[0]) 是自动计算的。
因此,您可以向列表中添加更多字符串,它们将相应地定位。

DrawingString class:

internal class DrawingString
{
    public DrawingString() { }
    public DrawingString(string text, string fontName, FontStyle style, float fontSize, Brush brush)
    {
        Text = text;
        FontName = fontName;
        FontStyle = style;
        FontSize = fontSize;
        Brush = brush;
    }
    public string Text { get; set; }
    public string FontName { get; set; }
    public FontStyle FontStyle { get; set; }
    public float FontSize { get; set; }
    public Brush Brush { get; set; }
}

List<DrawingString> drawingStrings = new List<DrawingString>() {
    new DrawingString("A Graphics", "Arial", FontStyle.Regular, 20f, Brushes.LightGreen),
    new DrawingString("String Drawing", "Segoe UI", FontStyle.Regular, 17f, Brushes.Orange),
    new DrawingString("Example", "Calibri", FontStyle.Regular, 22f, Brushes.SteelBlue),
    new DrawingString("One more Line", "Microsoft Sans Serif", FontStyle.Regular, 17f, Brushes.MediumPurple)
};

List<RectangleF> rectangles = new List<RectangleF>() {
    new RectangleF(5.8188976377953F, 5.48031496062993F, 170.59055118110234F, 170.6456692913386F)
};

您现在可以选择 canvas 来绘制这些字符串。它可以是控件或图像的表面。
如果它是一个 Control,使用 Paint 事件处理程序提供的 Graphics 对象来绘制字符串,否则,使用 Graphics.FromImage() 方法从 Bitmap 派生一个 Graphics 对象。

如果使用同一个StringFormat绘制所有字符串and/or如果需要用户或应用程序重新定义,声明一个StringFormat对象为Field并修改它根据需要(它也可以是 DrawingString class 的一部分):

StringFormat format = new StringFormat() {
    Alignment = StringAlignment.Center,
    LineAlignment = StringAlignment.Center,
    FormatFlags = StringFormatFlags.NoClip,
    Trimming = StringTrimming.EllipsisWord
};

假设这些字符串绘制在例如 PictureBox 控件的表面上,因此使用其 Paint 事件处理程序的 PaintEventArgs

private void somePictureBox_Paint(object sender, PaintEventArgs e)
{
    e.Graphics.TextRenderingHint = TextRenderingHint.ClearTypeGridFit;

    float interline = 12.0f;
    float textHeigh = drawingStrings.Select(ds => ds.FontSize + interline).Sum() - interline;
    float textPosition = (rectangles[0].Height - textHeigh + interline) / 2;

    foreach (var item in drawingStrings) {
        rectangles.Add(new RectangleF(
            new PointF(rectangles[0].X, textPosition), 
            new SizeF(rectangles[0].Width, item.FontSize)));
        textPosition += item.FontSize + interline;
    }

    // Uncomment to draw the bounding Rectangles border        
    // e.Graphics.DrawRectangles(Pens.White, rectangles.ToArray());

    int rectArea = 0;
    foreach (var item in drawingStrings) {
        rectArea += 1;
        using (Font font = new Font(item.FontName, item.FontSize, item.FontStyle, GraphicsUnit.Pixel)) {
            e.Graphics.DrawString(item.Text, font, item.Brush, rectangles[rectArea], format);
        }
    }
}

这是您在代码中看到的 List<DrawingString> 的结果,如果您添加另一行文本会发生什么:

如果取消注释此行:e.Graphics.DrawRectangles(Pens.White, rectangles.ToArray());,
您可以看到矩形的大小如何使用不同的 interline 值:

     Interline 12.0F          Interline 16.0F