Длина текста не соответствует FontSize [duplicate]

Посмотрите здесь

Текущая реализация сохраняет массив целых объектов для всех целых чисел от -5 до 256, когда вы создаете int в этом диапазоне вы фактически просто возвращаете ссылку на существующий объект.

62
задан user181813 17 April 2012 в 14:58
поделиться

7 ответов

Используйте класс 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.

121
ответ дан RandomEngy 19 August 2018 в 03:25
поделиться
  • 1
    Это было действительно полезно – Rakesh 23 December 2015 в 23:30
  • 2
    Как сделать то же самое в UWP – Arun Prasad 14 February 2017 в 16:43
  • 3
    @ArunPrasad. Было бы замечательно спросить по отдельному вопросу. Этот вопрос явно связан с WPF. – RandomEngy 14 February 2017 в 17:53

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

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

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

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

double d_width = MyText.Width;
3
ответ дан Chef Pharaoh 19 August 2018 в 03:25
поделиться
  • 1
    Вы можете сделать просто d_width = MyText.ActualWidth; без привязки. Проблема в том, что TextBlock еще не находится в визуальном дереве. – xmedeko 15 October 2015 в 19:38

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

/// <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);
}
33
ответ дан Hakam Fostok 19 August 2018 в 03:25
поделиться
  • 1
    Суммирование ширины глифов не будет работать для большинства шрифтов, так как оно не учитывает кернинг . – Nicolas Repiquet 19 April 2012 в 09:17
  • 2
    Хорошо ... У тебя есть идеи, как это решить? – Academy of Programmer 19 April 2012 в 12:53
  • 3
    С некоторыми незначительными изменениями это работало для меня, написав приложение для Windows 8.1 Store (Windows Runtime). Не забудьте установить информацию о шрифтах для этого TextBlock, чтобы он точно вычислял ширину. Аккуратное решение и стоит upvote. – David Rector 30 April 2016 в 09:37
  • 4
    Ключевым моментом здесь является создание этого временного текстового блока. Все эти манипуляции с существующим текстовым блоком на странице не работают: его ActualWidth обновляется только после перерисовки. – Tertium 3 March 2017 в 15:35

Представленное решение было подходящим для .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 Репозиторий GitHub с просвечиванием DPI.

Для полностью требуется дополнительная настройка . Поддержка масштабирования DPI с 10-летия Windows 10 (ниже кода), который я мог бы не использовать, t работать, но без него это работает на одном мониторе с настройкой масштабирования (и учитывает изменения масштабирования). Документ 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- Образцы, вам нужно добавить некоторые параметры в манифест приложения, чтобы включить возможность использования 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 для> = Windows 10 Anniversary Update 2) System & lt; Обновление для Windows 10 Anniversary

4
ответ дан mdip 19 August 2018 в 03:25
поделиться

Нашел это для вас:

Graphics g = control.CreateGraphics();
int width =(int)g.MeasureString(aString, control.Font).Width; 
g.dispose();
-2
ответ дан Setheron 19 August 2018 в 03:25
поделиться
33
ответ дан Hakam Fostok 30 October 2018 в 15:07
поделиться
0
ответ дан isxaker 30 October 2018 в 15:07
поделиться
Другие вопросы по тегам:

Похожие вопросы: