如何计算WPF文本块宽度为已知的字体大小和字符?

sd2nnvve  于 2022-12-24  发布在  其他
关注(0)|答案(7)|浏览(185)

假设我有TextBlock,文本为**“Some Text”**,字体大小为10.0
如何计算适当的TextBlock宽度

vddsk6oq

vddsk6oq1#

使用FormattedText类。
我在代码中创建了一个辅助函数:

private Size MeasureString(string candidate)
{
    var formattedText = new FormattedText(
        candidate,
        CultureInfo.CurrentCulture,
        FlowDirection.LeftToRight,
        new Typeface(this.textBlock.FontFamily, this.textBlock.FontStyle, this.textBlock.FontWeight, this.textBlock.FontStretch),
        this.textBlock.FontSize,
        Brushes.Black,
        new NumberSubstitution(),
        VisualTreeHelper.GetDpi(this.textBlock).PixelsPerDip);

    return new Size(formattedText.Width, formattedText.Height);
}

它返回可在WPF布局中使用的与设备无关的像素。

vcirk6k6

vcirk6k62#

为了记录...我假设操作员试图通过编程来确定文本块在添加到可视化树后将占用的宽度。我认为一个比formatedText更好的解决方案(如何处理类似于文本换行的东西?)是在示例文本块上使用Measure和Arrange。

var textBlock = new TextBlock { Text = "abc abd adfdfd", TextWrapping = TextWrapping.Wrap };
// auto sized
textBlock.Measure(new Size(Double.PositiveInfinity, Double.PositiveInfinity));
textBlock.Arrange(new Rect(textBlock.DesiredSize));

Debug.WriteLine(textBlock.ActualWidth); // prints 80.323333333333
Debug.WriteLine(textBlock.ActualHeight);// prints 15.96

// constrain the width to 16
textBlock.Measure(new Size(16, Double.PositiveInfinity));
textBlock.Arrange(new Rect(textBlock.DesiredSize));

Debug.WriteLine(textBlock.ActualWidth); // prints 14.58
Debug.WriteLine(textBlock.ActualHeight);// prints 111.72
vfwfrxfs

vfwfrxfs3#

所提供的解决方案适用于. Net Framework 4.5,但是,随着Windows 10 DPI扩展和Framework 4.6.x为其添加不同程度的支持,用于度量文本的构造函数现在标记为[Obsolete],以及该方法上不包含pixelsPerDip参数的任何构造函数。
不幸的是,这有点复杂,但新的缩放功能会带来更高的精度。

每次下降的像素数

根据MSDN,这表示:
每个密度无关像素的像素值,相当于缩放因子。例如,如果屏幕的DPI为120(或1.25,因为120/96 = 1.25),则每个密度无关像素绘制1.25个像素。DIP是WPF使用的度量单位,与设备分辨率和DPI无关。
以下是我根据具有DPI缩放感知功能的Microsoft/WPF-Samples GitHub存储库的指导实现的选定答案。
自Windows 10周年纪念日起,* 完全 * 支持DPI扩展还需要一些额外配置(在代码下面),我不能去工作,但如果没有它,它可以在配置了缩放的单个监视器上工作(并考虑缩放变化)。上述存储库中的Word文档是这些信息的来源,因为一旦添加了这些值,应用程序就不会启动。来自同一回购协议的This sample code也用作良好的参考点。

public partial class MainWindow : Window
{
    private DpiScale m_dpiInfo;
    private readonly object m_sync = new object();

    public MainWindow()
    {
        InitializeComponent();
        Loaded += OnLoaded;
    }
    
    private Size MeasureString(string candidate)
    {
        DpiScale dpiInfo;
        lock (m_dpiInfo)
            dpiInfo = m_dpiInfo;

        if (dpiInfo == null)
            throw new InvalidOperationException("Window must be loaded before calling MeasureString");

        var formattedText = new FormattedText(candidate, CultureInfo.CurrentUICulture,
                                              FlowDirection.LeftToRight,
                                              new Typeface(this.textBlock.FontFamily, 
                                                           this.textBlock.FontStyle, 
                                                           this.textBlock.FontWeight, 
                                                           this.textBlock.FontStretch),
                                              this.textBlock.FontSize,
                                              Brushes.Black, 
                                              dpiInfo.PixelsPerDip);
        
        return new Size(formattedText.Width, formattedText.Height);
    }

// ... The Rest of Your Class ...

    /*
     * Event Handlers to get initial DPI information and to set new DPI information
     * when the window moves to a new display or DPI settings get changed
     */
    private void OnLoaded(object sender, RoutedEventArgs e)
    {            
        lock (m_sync)
            m_dpiInfo = VisualTreeHelper.GetDpi(this);
    }

    protected override void OnDpiChanged(DpiScale oldDpiScaleInfo, DpiScale newDpiScaleInfo)
    {
        lock (m_sync)
            m_dpiInfo = newDpiScaleInfo;

        // Probably also a good place to re-draw things that need to scale
    }
}

其他要求

根据Microsoft/WPF-Samples上的文档,您需要向应用程序清单中添加一些设置,以涵盖Windows 10周年纪念在多显示器配置中每个显示器具有不同DPI设置的功能。可以合理地猜测,如果没有这些设置,当窗口从一个显示器移动到另一个具有不同设置的显示器时,可能不会引发OnDpiChanged事件。这将使您的测量继续依赖于以前的DpiScale。我正在编写的应用程序是为我自己编写的,我没有这样的设置,所以我没有什么可测试的,当我按照指导进行测试时,我最终得到了一个由于明显错误而无法启动的应用程序,所以我放弃了,但最好仔细查看并调整您的应用清单,使其包含:

<application xmlns="urn:schemas-microsoft-com:asm.v3">
    <windowsSettings>
        <dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">true</dpiAware>
        <dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">PerMonitor</dpiAwareness>
    </windowsSettings>
</application>

根据文档:
[这]两个标记的组合具有以下效果:1)Windows 10周年更新的每台显示器2)系统〈Windows 10周年更新

js4nwp54

js4nwp544#

我通过在后端代码中添加到元素的绑定路径来解决这个问题:

<TextBlock x:Name="MyText" Width="{Binding Path=ActualWidth, ElementName=MyText}" />

我发现这是一个更干净的解决方案,而不是将上面引用(如FormattedText)的所有开销添加到代码中。
之后,我就能做到这一点:

double d_width = MyText.Width;
oxiaedzo

oxiaedzo5#

我发现了一些很有效的方法...

/// <summary>
/// Get the required height and width of the specified text. Uses Glyph's
/// </summary>
public static Size MeasureText(string text, FontFamily fontFamily, FontStyle fontStyle, FontWeight fontWeight, FontStretch fontStretch, double fontSize)
{
    Typeface typeface = new Typeface(fontFamily, fontStyle, fontWeight, fontStretch);
    GlyphTypeface glyphTypeface;

    if (!typeface.TryGetGlyphTypeface(out glyphTypeface))
    {
        return MeasureTextSize(text, fontFamily, fontStyle, fontWeight, fontStretch, fontSize);
    }

    double totalWidth = 0;
    double height = 0;

    for (int n = 0; n < text.Length; n++)
    {
        ushort glyphIndex = glyphTypeface.CharacterToGlyphMap[text[n]];

        double width = glyphTypeface.AdvanceWidths[glyphIndex] * fontSize;

        double glyphHeight = glyphTypeface.AdvanceHeights[glyphIndex] * fontSize;

        if (glyphHeight > height)
        {
            height = glyphHeight;
        }

        totalWidth += width;
    }

    return new Size(totalWidth, height);
}

/// <summary>
/// Get the required height and width of the specified text. Uses FortammedText
/// </summary>
public static Size MeasureTextSize(string text, FontFamily fontFamily, FontStyle fontStyle, FontWeight fontWeight, FontStretch fontStretch, double fontSize)
{
    FormattedText ft = new FormattedText(text,
                                            CultureInfo.CurrentCulture,
                                            FlowDirection.LeftToRight,
                                            new Typeface(fontFamily, fontStyle, fontWeight, fontStretch),
                                            fontSize,
                                            Brushes.Black);
    return new Size(ft.Width, ft.Height);
}
esbemjvw

esbemjvw6#

我用这个:

var typeface = new Typeface(textBlock.FontFamily, textBlock.FontStyle, textBlock.FontWeight, textBlock.FontStretch);
var formattedText = new FormattedText(textBlock.Text, Thread.CurrentThread.CurrentCulture, textBlock.FlowDirection, typeface, textBlock.FontSize, textBlock.Foreground);

var size = new Size(formattedText.Width, formattedText.Height)
cpjpxq1n

cpjpxq1n7#

找到这个给你:

Graphics g = control.CreateGraphics();
int width =(int)g.MeasureString(aString, control.Font).Width; 
g.dispose();

相关问题