Подтвердить что ты не робот

Как рассчитать ширину текстового блока WPF для его известного размера шрифта и символов?

Скажем, у меня TextBlock с текстом "Некоторый текст и размер шрифта 10.0.

Как я могу рассчитать подходящую TextBlock ширину?

4b9b3361

Ответ 1

Используйте класс 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(),
        1);

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

Он возвращает независимые от устройства пиксели, которые можно использовать в макете WPF.

Ответ 2

Для записи... Я предполагаю, что op'er пытается запрограммировать ширину, которую будет использовать textBlock после добавления в визуальное дерево. IMO лучшее решение, а затем formattedText (как вы обрабатываете что-то вроде textWrapping?) Было бы использовать Measure и Arrange на образце TextBlock. например.

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

Ответ 3

Однако предоставленное решение было подходящим для.Net Framework 4.5, однако с масштабированием Windows 10 DPI и Framework 4.6.x, добавляя к нему различную степень поддержки, конструктор, используемый для измерения текста, теперь помечен как [Obsolete], а также любые конструкторы на этом метод, который не включает параметр pixelsPerDip.

К сожалению, это немного больше, но это приведет к большей точности с новыми возможностями масштабирования.

PixelsPerDip

Согласно MSDN, это означает:

Значение Pixels Per Density Independent Pixel, эквивалентное масштабному коэффициенту. Например, если DPI экрана равен 120 (или 1,25, потому что 120/96 = 1,25), рисование составляет 1,25 пикселя на отдельный пиксель. DIP - это единица измерения, используемая WPF, чтобы быть независимой от разрешения устройства и DPI.

Здесь моя реализация выбранного ответа основана на руководстве из репозитория Microsoft/WPF-Samples GitHub с масштабированием уровня DPI.

Для полной поддержки масштабирования DPI с Windows 10 Anniversary (ниже кода) требуется некоторая дополнительная конфигурация, которую я не мог бы работать, но без нее это работает на одном мониторе с настройкой масштабирования (и учитывает изменения масштабирования). Документ Word в вышеуказанном репо является источником этой информации, так как мое приложение не запускается после добавления этих значений. Этот пример кода из того же репо также служил хорошей точкой отсчета.

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)
    {
        DpiInfo 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 Anniversary для установки разных параметров 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) Per-Monitor для> = Обновление 10-летней версии Windows 2) Система <Обновление 10-летней версии Windows

Ответ 4

Я решил это, добавив путь привязки к элементу во внутреннем коде:

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

Я нашел, что это гораздо более чистое решение, чем добавление всех служебных данных вышеупомянутых ссылок, таких как FormattedText, в мой код.

После этого я смог это сделать:

double d_width = MyText.Width;

Ответ 5

Я нашел несколько методов, которые отлично работают...

/// <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);
}

Ответ 6

Я использую это:

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)

Ответ 7

Найдено для вас:

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