【问题标题】:Rendering text in WPF so that it perfectly fits in a given rectangle在 WPF 中渲染文本,使其完全适合给定的矩形
【发布时间】:2011-01-21 17:20:27
【问题描述】:

我需要在 WPF 画布上以完全适合预定义框的方式显示单词。

一个框通常包含一行文本,从一个字母到几个单词。

框内的文字必须尽可能大,即:触及框的所有边框(除非可能由于框宽/高比异常而导致文本失真过多)。

我找不到根据文本内容计算适当字体高度、缩放和偏移的好方法。

原始文本宽高比不能改变的第一个解决方案已经很不错了!

我想使用 TextBlock 元素,但其他任何可行的都应该没问题。

【问题讨论】:

    标签: c# wpf .net-3.5


    【解决方案1】:

    正如 Robery Levy 的回答所说,您可以使用 Viewbox 来实现此目的。但是,文本本身不会拉伸,因此根据您的文本(如您所见),您仍然会在零个或多个边上保留一些“边距”。要解决此问题,您可以创建一个自定义控件,该控件从 FormattedText 构建一个 Geometry,然后在 OnRender 中使用 DrawGeometry 绘制它。您会注意到文本质量如何随着FontSize 的增大而提高。一个非常小的文本(例如FontSize="10")在大的Viewbox 中看起来不会很清晰,所以你必须尝试一下

    一些示例 Xaml

    <Canvas Background="Black">
        <Viewbox Canvas.Left="10" Canvas.Top="10"
                 Stretch="Fill" Width="200" Height="50">
            <Border Background="Red">
                <local:StretchText Text="Text" Foreground="Green" FontSize="100"/>
            </Border>
        </Viewbox>
        <Viewbox Canvas.Left="230" Canvas.Top="10"
                 Stretch="Fill" Width="200" Height="50">
            <Border Background="Red">
                <local:StretchText Text="B" Foreground="Green" FontSize="500"/>
            </Border>
        </Viewbox>
    </Canvas>
    

    StretchText.cs

    public class StretchText : Control
    {
        protected override void OnRender(DrawingContext drawingContext)
        {
            FormattedText formattedText = new FormattedText(
                Text,
                CultureInfo.GetCultureInfo("en-us"),
                FlowDirection.LeftToRight,
                new Typeface(FontFamily, FontStyle, FontWeight, FontStretches.Normal),
                FontSize,
                Foreground);
    
            Geometry textGeometry = formattedText.BuildGeometry(new Point(0, 0));
            this.MinWidth = textGeometry.Bounds.Width;
            this.MinHeight = textGeometry.Bounds.Height;
    
            TranslateTransform translateTransform = new TranslateTransform();
            translateTransform.X = -textGeometry.Bounds.Left;
            translateTransform.Y = -textGeometry.Bounds.Top;
            drawingContext.PushTransform(translateTransform);
            drawingContext.DrawGeometry(Foreground, new Pen(Foreground, 1.0), textGeometry);
        }
    
        public string Text
        {
            get { return (string)GetValue(TextProperty); }
            set { SetValue(TextProperty, value); }
        }
        public static readonly DependencyProperty TextProperty =
            DependencyProperty.Register("Text",
            typeof(string),
            typeof(StretchText),
            new FrameworkPropertyMetadata(string.Empty, FrameworkPropertyMetadataOptions.AffectsRender));
    }
    

    【讨论】:

    • 谢谢!我对其进行了测试,并且效果很好。与此同时,我找到了另一种解决方案:通过临时 FormattedText 对象测量文本元素(感谢属性“Width”、“Extent”和“OverhangAfter”),然后相应地将 Scale+TranslateTransform 应用于每个元素。但是,它有两个缺点:实际的文本块对象变得太大(弄乱工具提示),并且渲染没有我想要的那么快。
    • @Jem:没问题 :) 我们的解决方案之间的渲染时间如何?在这两种情况下都“放慢”了吗?我在Grid 中同时尝试了 10 个左右的StretchText,然后快速调整了Window 的大小。我似乎工作得很好,但 DrawGeometry 以非常慢而闻名
    • 要显示的字数大约是一两千,但是用你的解决方案调整页面大小已经足够流畅了。我会说它比我的快 3 倍,而且它很稳定(没有“滞后”)。但是,对于小文本,文本质量没有那么清晰(可能是因为它不再是“文本”,而是“几何”),但已经足够好了。理想情况下,我想要更好的性能和最好的文本质量,但您的解决方案是目前最好的折衷方案。
    • @Jem: 好棒 :) 对于文本质量,如果你还没有尝试过,尝试增加字体大小以获得更清晰的文本。
    【解决方案2】:

    【讨论】:

    • 谢谢,刚试过。有趣,但它并不能完全满足我的需要:它不知道文本内容的原样。例如,除非字母越过基线(例如'g'、'p'、'y'、'q'),否则我在文本下仍然有边距。我真的需要输入框是文本“边界框”。
    【解决方案3】:

    您可以查看 Charles Petzold 的文章“Render Text on a Path with WPF”。不幸的是,由于 MSDN 网站出现问题,我目前无法刷新有关主题的知识,但他描述了如何在路径内缩放文本。

    【讨论】:

      猜你喜欢
      • 2014-12-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-08-06
      • 1970-01-01
      • 1970-01-01
      • 2015-06-03
      相关资源
      最近更新 更多