@ArunPrasad Было бы неплохо задать отдельный вопрос. Этот вопрос явно относится к WPF.
RandomEngy
Это возвращает нулевую ширину, если переданный кандидат является пробелом. Я думаю, это может быть связано с переносом слов?
Марк Миллер
43
Для записи ... Я предполагаю, что оператор пытается программно определить ширину, которую textBlock займет после добавления в визуальное дерево. ИМО лучшим решением, чем formattedText (как вы обрабатываете что-то вроде textWrapping?), Было бы использовать Measure and 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
С небольшими изменениями у меня это сработало, когда я написал приложение Windows 8.1 Store (Windows Runtime). Не забудьте установить информацию о шрифте для этого TextBlock, чтобы он точно рассчитывал ширину. Отличное решение, за которое стоит проголосовать.
Дэвид Ректор
1
Ключевым моментом здесь является создание этого временного текстового блока на месте. Все эти манипуляции с существующим текстовым блоком на странице не работают: его ActualWidth обновляется только после перерисовки.
Tertium
13
Предоставленное решение было подходящим для .Net Framework 4.5, однако с масштабированием Windows 10 DPI и Framework 4.6.x, добавляющим различную степень поддержки для него, конструктор, используемый для измерения текста, теперь помечен [Obsolete]вместе с любыми конструкторами этого метода, которые не включатьpixelsPerDip параметр.
К сожалению, это немного сложнее, но это приведет к большей точности с новыми возможностями масштабирования.
### PixelsPerDip
Согласно MSDN, это означает:
Значение пикселей, не зависящих от плотности, эквивалентное масштабному коэффициенту. Например, если DPI экрана составляет 120 (или 1,25, потому что 120/96 = 1,25), отрисовывается 1,25 пикселя на пиксель, независимый от плотности. DIP - это единица измерения, используемая WPF для независимости от разрешения устройства и DPI.
Вот моя реализация выбранного ответа на основе рекомендаций из репозитория Microsoft / WPF-Samples GitHub с поддержкой масштабирования DPI.
Для полной поддержки масштабирования DPI начиная с Windows 10 Anniversary (под кодом) требуется некоторая дополнительная конфигурация , которую я не мог заставить работать, но без нее это работает на одном мониторе с настроенным масштабированием (и с учетом изменений масштабирования). Документ Word в репозитории выше является источником этой информации, поскольку мое приложение не запускалось после добавления этих значений. Этот пример кода из того же репозитория также послужил хорошей точкой отсчета.
publicpartialclassMainWindow : Window
{
private DpiScale m_dpiInfo;
privatereadonlyobject m_sync = newobject();
publicMainWindow()
{
InitializeComponent();
Loaded += OnLoaded;
}
private Size MeasureString(string candidate)
{
DpiScale dpiInfo;
lock (m_dpiInfo)
dpiInfo = m_dpiInfo;
if (dpiInfo == null)
thrownew 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);
returnnew 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
*/privatevoidOnLoaded(object sender, RoutedEventArgs e)
{
lock (m_sync)
m_dpiInfo = VisualTreeHelper.GetDpi(this);
}
protectedoverridevoidOnDpiChanged(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. Приложение, которое я писал, предназначалось только мне, и у меня нет такой настройки, поэтому мне не с чем было тестировать, и когда я следовал инструкциям, у меня было приложение, которое не запускалось из-за манифеста ошибок, поэтому я сдался, но было бы неплохо просмотреть это и настроить манифест приложения, чтобы он содержал:
Я обнаружил, что это гораздо более чистое решение, чем добавление всех накладных расходов на приведенные выше ссылки, такие как FormattedText, в мой код.
Можно просто обойтись d_width = MyText.ActualWidth;без привязки. Проблема в TextBlockтом, что в визуальном дереве еще нет.
xmedeko
0
Я использую такой:
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)
ActualWidth
.Ответы:
Воспользуйтесь
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.
источник
Для записи ... Я предполагаю, что оператор пытается программно определить ширину, которую textBlock займет после добавления в визуальное дерево. ИМО лучшим решением, чем formattedText (как вы обрабатываете что-то вроде textWrapping?), Было бы использовать Measure and 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
источник
Предоставленное решение было подходящим для .Net Framework 4.5, однако с масштабированием Windows 10 DPI и Framework 4.6.x, добавляющим различную степень поддержки для него, конструктор, используемый для измерения текста, теперь помечен
[Obsolete]
вместе с любыми конструкторами этого метода, которые не включатьpixelsPerDip
параметр.К сожалению, это немного сложнее, но это приведет к большей точности с новыми возможностями масштабирования.
### PixelsPerDip
Согласно MSDN, это означает:
Вот моя реализация выбранного ответа на основе рекомендаций из репозитория 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) { 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 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>
По документации:
источник
Я нашел несколько методов, которые работают нормально ...
/// <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); }
источник
Я решил это, добавив путь привязки к элементу во внутреннем коде:
<TextBlock x:Name="MyText" Width="{Binding Path=ActualWidth, ElementName=MyText}" />
Я обнаружил, что это гораздо более чистое решение, чем добавление всех накладных расходов на приведенные выше ссылки, такие как FormattedText, в мой код.
После этого я смог сделать это:
double d_width = MyText.Width;
источник
d_width = MyText.ActualWidth;
без привязки. Проблема вTextBlock
том, что в визуальном дереве еще нет.Я использую такой:
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)
источник
Нашел это для вас:
Graphics g = control.CreateGraphics(); int width =(int)g.MeasureString(aString, control.Font).Width; g.dispose();
источник