【问题标题】:How to get the exact text margins used by TextRenderer如何获取 TextRenderer 使用的确切文本边距
【发布时间】:2011-05-24 14:55:16
【问题描述】:

System.Windows.Forms.TextRenderer.DrawText 方法根据flags 参数的值呈现带或不带左右填充的格式化文本:

  • TextFormatFlags.NoPadding - 将文本紧密地放入边界框,
  • TextFormatFlags.GlyphOverhangPadding - 添加一些左右边距,
  • TextFormatFlags.LeftAndRightPadding - 增加了更大的利润。

现在,我的问题是对于给定的设备上下文、字符串、字体等,我如何才能获得DrawText 添加到文本中的确切填充量(左右)? p>

我已经使用 .NET Reflector 深入研究了 .NET 4,发现 TextRenderer 计算“悬垂填充”,即字体高度的 1/6,然后将该值相乘以使用这些系数计算左右边距:

  • TextFormatFlags.GlyphOverhangPadding 左 1.0,右 1.5,
  • TextFormatFlags.LeftAndRightPadding 左 2.0,右 2.5。

结果值向上取整并传递给DrawTextExADrawTextExW 原生API 函数。很难重新创建这个过程,因为字体的高度不是来自System.Drawing.Font,而是来自System.Windows.Forms.Internal.WindowsFont,并且这些类为相同的字体返回不同的值。并且涉及到来自 System.Windows.Forms.Internal 命名空间的许多其他内部 BCL 类。反编译所有这些并在我的应用程序中重用它们的代码不是一种选择,因为这将是一个严重的 .NET 实现依赖项。这就是为什么我需要知道 WinForms 中是否有一些公共 API,或者至少我可以使用哪些 Windows 函数来获取左右边距的值。


注意:我尝试TextRenderer.MeasureText 使用和不使用填充并比较结果,但这只给了我左右边距的总和,我需要单独使用它们。


注意 2: 如果您想知道我为什么需要这个:我想用多种字体/颜色绘制一个字符串。这涉及使用NoPadding 选项为每个统一格式的子字符串调用一次DrawText(以便文本不会传播),但我还想在整个多的开头和结尾手动添加普通GlyphOverhangPadding格式化文本。

【问题讨论】:

  • +1 用于综合分析 :-)。我也遇到了这个问题,你嗅到的左填充值为我解决了这个问题 - 谢谢。

标签: c# winforms gdi


【解决方案1】:

计算左右边距所需的值是TEXTMETRIC.tmHeight,可以使用Win32 API获得。

但是,我发现 tmHeight 只是字体的行高(以像素为单位),因此这三种方法将为您提供相同的值(您可以在代码中使用任何您喜欢的值):

int apiHeight = GetTextMetrics(graphics, font).tmHeight;
int gdiHeight = TextRenderer.MeasureString(...).Height;
int gdipHeight = (int)Math.Ceiling(font.GetHeight(graphics));

为了获得左右边距,我们使用与 TextRenderer 相同的代码:

private const float ItalicPaddingFactor = 0.5f;

...

float overhangPadding = (gdiHeight / 6.0f);

//NOTE: proper margins for TextFormatFlags.LeftAndRightPadding flag
//int leftMargin = (int)Math.Ceiling(overhangPadding);
//int rightMargin = (int)Math.Ceiling(overhangPadding * (2 + ItalicPaddingFactor));

//NOTE: proper margins for TextFormatFlags.GlyphOverhangPadding flag
int leftMargin = (int)Math.Ceiling(overhangPadding);
int rightMargin = (int)Math.Ceiling(overhangPadding * (1 + ItalicPaddingFactor));

Size sizeOverhangPadding = TextRenderer.MeasureText(e.Graphics, "ABC", font, Size.Empty, TextFormatFlags.GlyphOverhangPadding);
Size sizeNoPadding = TextRenderer.MeasureText(e.Graphics, "ABC", font, Size.Empty, TextFormatFlags.NoPadding);

int overallPadding = (sizeOverhangPadding.Width - sizeNoPadding.Width);

现在您可以轻松检查了

(leftMargin + rightMargin) == overallPadding

请注意:

我需要解决这个问题,以便在使用 GDI 文本呈现的 ListView-based control 中实现“搜索突出显示”功能:

像魅力一样工作:)

【讨论】:

    【解决方案2】:

    此答案摘自此处 - http://www.techyv.com/questions/how-get-exact-text-margins-used-textrenderer#comment-35164

    如果您曾经希望 Windows 窗体中的 Label 或 TextBox 表现得更像 Web,那么您可能已经发现没有直观的方法可以让 Label 或 TextBox 自动调整其高度以适应它包含的文本。虽然它可能不直观,但绝对不是不可能的。

    在本例中,我将使用停靠在窗体顶部的 TextBox(您可以轻松地使用标签)。要使用它,请将名为 MyTextBox 的文本框添加到窗体中,并将​​ Dock 设置为 DockStyle 。最佳。将 TextBox 的 Resize 事件连接到此事件处理程序。

    private void MyTextBox_Resize( object sender, EventArgs e )
    {
        // Grab a reference to the TextBox
        TextBox tb = sender as TextBox; 
    
        // Figure out how much space is used for borders, etc. 
        int chromeHeight = tb.Height - tb.ClientSize.Height;
    
        // Create a proposed size that is very tall, but exact in width. 
        Size proposedSize = new Size( tb.ClientSize.Width, int.MaxValue );
    
        // Measure the text using the TextRenderer
        Size textSize = TextRenderer.MeasureText( tb.Text, tb.Font,
            proposedSize, TextFormatFlags.TextBoxControl
             | TextFormatFlags.WordBreak );
    
        // Adjust the height to include the text height, chrome height,
        // and vertical margin
        tb.Height = chromeHeight + textSize.Height 
            + tb.Margin.Vertical; 
    }
    

    如果您想调整未停靠的标签或文本框(例如,在 FlowLayoutPanel 或其他面板中,或者只是放置在表单上的标签或文本框),那么您甚至可以处理表单的调整大小,并且直接修改Control的属性即可。

    【讨论】:

    【解决方案3】:

    这可能看起来(非常)粗糙,但这是我能想到的唯一原生实现: DrawText 绘制到 IDeviceContext,由 Graphics 实现。现在,我们可以使用以下代码来利用它:

    Bitmap bmp = new Bitmap(....);
    Graphics graphic = Graphics.FromImage(bmp);
    textRenderer.DrawText(graphic,....);
    graphic.Dispose();
    

    使用新的Bitmap,您可以逐个像素地按某些条件计算它们。 再说一次,这种方法非常粗暴和浪费,但至少它是原生的....

    这未经测试,但基于以下来源:

    http://msdn.microsoft.com/en-us/library/4ftkekek.aspx

    http://msdn.microsoft.com/en-us/library/system.drawing.idevicecontext.aspx

    http://msdn.microsoft.com/en-us/library/system.drawing.graphics.aspx

    http://www.pcreview.co.uk/forums/graphics-bitmap-t1399954.html

    【讨论】:

      【解决方案4】:

      几年前我做过类似的事情,以突出显示搜索结果(搜索模式以粗体显示等)。我的实现是在 DevExpress 中,所以代码可能不相关。如果你觉得有用我可以复制它,只需要找到那个实现。

      System.Windows.Forms 中,要使用的类是Graphics。它有一个MeasureCharacterRanges() 接受StringFormat 的方法(从GenericTypographic 开始,然后从那里开始)。它比 TextRenderer 更适合通过链接具有不同样式、字体或画笔的部分来显示完整的字符串。

      在实际填充测量方面,您比我走得更远。 DevExpress 的控件为您提供了开始的文本边界矩形,因此为我完成了。

      Here's Pierre Arnaud 为我在 Google 上发表的一篇文章,涉及该领域。不幸的是,那里的 GDI+“血腥细节”链接已损坏。

      干杯,
      乔诺

      【讨论】:

        【解决方案5】:

        解决方法是计算 MeasureText 将要添加的内容:

        var formatFlags FormatFlags =
            TextFormatFlags.NoPadding |
            TextFormatFlags.SingleLine;
        
        int largeWidth = TextRenderer.MeasureText(
            "  ",
            font,
            new Size(int.MaxValue, int.MaxValue),
            formatFlags
        ).Width;
        int smallWidth = TextRenderer.MeasureText(
            " ",
            font,
            new Size(int.MaxValue, int.MaxValue),
            formatFlags
        ).Width;
        
        int extra = smallWidth - (largeWidth - smallWidth);
        

        我们计算一个空格的宽度和两个空格的宽度。两者都添加了额外的宽度,因此我们可以推断正在添加的额外宽度。添加的宽度显然总是相同的,因此从MeasureText 返回的每个宽度中减去extra 会得到预期的结果。

        【讨论】:

          猜你喜欢
          • 2014-05-30
          • 1970-01-01
          • 1970-01-01
          • 2020-08-31
          • 2018-01-18
          • 1970-01-01
          • 2022-09-23
          • 1970-01-01
          • 2017-09-01
          相关资源
          最近更新 更多