Посмотрите здесь
Текущая реализация сохраняет массив целых объектов для всех целых чисел от -5 до 256, когда вы создаете int в этом диапазоне вы фактически просто возвращаете ссылку на существующий объект.
Используйте класс 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 x:Name="MyText" Width="{Binding Path=ActualWidth, ElementName=MyText}" />
Я нашел, что это намного более чистое решение, чем добавление всех накладных расходов вышеупомянутых ссылок, таких как FormattedText, мой код.
После этого я смог сделать это:
double d_width = MyText.Width;
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);
}
Представленное решение было подходящим для .Net Framework 4.5, однако, с масштабированием Windows 10 DPI и Framework 4.6.x, добавляя к нему различные степени поддержки, конструктор, используемый для измерения текста, теперь отмечен [Obsolete]
, а также любой конструкторы этого метода, которые не включают параметр 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
Нашел это для вас:
Graphics g = control.CreateGraphics();
int width =(int)g.MeasureString(aString, control.Font).Width;
g.dispose();