@ArunPrasad To byłaby doskonała rzecz do zadania osobnego pytania. To pytanie jest wyraźnie ograniczone do WPF.
RandomEngy,
Zwraca to szerokość równą zero, jeśli przekazany kandydat jest spacją. Myślę, że może to być związane z zawijaniem słów?
Mark Miller
43
Dla przypomnienia ... Zakładam, że operator próbuje programowo określić szerokość, jaką textBlock zajmie po dodaniu do drzewa wizualnego. IMO lepszym rozwiązaniem niż formattedText (jak sobie radzić z czymś takim jak textWrapping?) Byłoby użycie opcji Measure and Arrange na przykładowym TextBlock. na przykład
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
Po kilku drobnych modyfikacjach zadziałało to dla mnie przy pisaniu aplikacji Sklepu Windows 8.1 (Windows Runtime). Nie zapomnij ustawić informacji o czcionce dla tego TextBlock, aby dokładnie obliczał szerokość. Zgrabne rozwiązanie i warte uznania.
David Rector
1
Kluczową kwestią jest tutaj utworzenie tymczasowego bloku tekstu w miejscu. Wszystkie te manipulacje z istniejącym blokiem tekstowym na stronie nie działają: jego ActualWidth jest aktualizowana dopiero po ponownym narysowaniu.
Tertium
13
Dostarczone rozwiązanie było odpowiednie dla .Net Framework 4.5, jednak ze skalowaniem Windows 10 DPI i Framework 4.6.x dodającym różne stopnie obsługi, konstruktor używany do pomiaru tekstu jest teraz oznaczony [Obsolete]wraz z wszystkimi konstruktorami tej metody, które to robią. nie obejmująpixelsPerDip parametru.
Niestety jest to trochę bardziej skomplikowane, ale zaowocuje większą dokładnością dzięki nowym możliwościom skalowania.
### PixelsPerDip
Według MSDN oznacza to:
Wartość niezależnych pikseli pikseli na gęstość, która jest odpowiednikiem współczynnika skali. Na przykład, jeśli rozdzielczość ekranu wynosi 120 (lub 1,25, ponieważ 120/96 = 1,25), rysowany jest 1,25 piksela na piksel niezależny od gęstości. DIP to jednostka miary używana przez WPF, która ma być niezależna od rozdzielczości urządzenia i DPI.
Oto moja implementacja wybranej odpowiedzi na podstawie wskazówek z repozytorium Microsoft / WPF-Samples GitHub ze świadomością skalowania DPI.
Do pełnej obsługi skalowania DPI od rocznicy Windows 10 (poniżej kodu) wymagana jest dodatkowa konfiguracja , której nie mogłem uruchomić, ale bez niej działa to na jednym monitorze ze skonfigurowanym skalowaniem (i uwzględnia zmiany skalowania). Dokument Worda w powyższym repozytorium jest źródłem tych informacji, ponieważ moja aplikacja nie uruchomiła się po dodaniu tych wartości. Ten przykładowy kod z tego samego repozytorium służył również jako dobry punkt odniesienia.
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
}
}
Inne wymagania
Zgodnie z dokumentacją w Microsoft / WPF-Samples, musisz dodać pewne ustawienia do manifestu aplikacji, aby objąć zdolność Windows 10 Anniversary do posiadania różnych ustawień DPI na ekran w konfiguracjach z wieloma monitorami. Można się domyślać, że bez tych ustawień zdarzenie OnDpiChanged może nie zostać wywołane, gdy okno jest przenoszone z jednego ekranu na inny z innymi ustawieniami, co spowodowałoby, że pomiary nadal opierałyby się na poprzednich DpiScale. Aplikacja, którą pisałem, była dla mnie, sama i nie mam takiej konfiguracji, więc nie miałem z czym testować, a kiedy zastosowałem się do wskazówek, skończyło się na aplikacji, która nie mogła się uruchomić z powodu manifestu błędy, więc zrezygnowałem, ale warto przyjrzeć się temu i dostosować manifest aplikacji, aby zawierał:
Połączenie [tych] dwóch tagów ma następujący efekt: 1) Per-Monitor dla> = Rocznicowa aktualizacja systemu Windows 10 2) System <Rocznicowa aktualizacja systemu Windows 10
Możesz to zrobić po prostu d_width = MyText.ActualWidth;bez wiązania. Problem polega na tym, że TextBlocknie ma jeszcze drzewa wizualnego.
xmedeko
0
Używam tego:
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
.Odpowiedzi:
Skorzystaj z
FormattedText
klasy.Zrobiłem funkcję pomocniczą w moim kodzie:
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); }
Zwraca piksele niezależne od urządzenia, których można używać w układzie WPF.
źródło
Dla przypomnienia ... Zakładam, że operator próbuje programowo określić szerokość, jaką textBlock zajmie po dodaniu do drzewa wizualnego. IMO lepszym rozwiązaniem niż formattedText (jak sobie radzić z czymś takim jak textWrapping?) Byłoby użycie opcji Measure and Arrange na przykładowym TextBlock. na przykład
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
źródło
Dostarczone rozwiązanie było odpowiednie dla .Net Framework 4.5, jednak ze skalowaniem Windows 10 DPI i Framework 4.6.x dodającym różne stopnie obsługi, konstruktor używany do pomiaru tekstu jest teraz oznaczony
[Obsolete]
wraz z wszystkimi konstruktorami tej metody, które to robią. nie obejmująpixelsPerDip
parametru.Niestety jest to trochę bardziej skomplikowane, ale zaowocuje większą dokładnością dzięki nowym możliwościom skalowania.
### PixelsPerDip
Według MSDN oznacza to:
Oto moja implementacja wybranej odpowiedzi na podstawie wskazówek z repozytorium Microsoft / WPF-Samples GitHub ze świadomością skalowania DPI.
Do pełnej obsługi skalowania DPI od rocznicy Windows 10 (poniżej kodu) wymagana jest dodatkowa konfiguracja , której nie mogłem uruchomić, ale bez niej działa to na jednym monitorze ze skonfigurowanym skalowaniem (i uwzględnia zmiany skalowania). Dokument Worda w powyższym repozytorium jest źródłem tych informacji, ponieważ moja aplikacja nie uruchomiła się po dodaniu tych wartości. Ten przykładowy kod z tego samego repozytorium służył również jako dobry punkt odniesienia.
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 } }
Inne wymagania
Zgodnie z dokumentacją w Microsoft / WPF-Samples, musisz dodać pewne ustawienia do manifestu aplikacji, aby objąć zdolność Windows 10 Anniversary do posiadania różnych ustawień DPI na ekran w konfiguracjach z wieloma monitorami. Można się domyślać, że bez tych ustawień zdarzenie OnDpiChanged może nie zostać wywołane, gdy okno jest przenoszone z jednego ekranu na inny z innymi ustawieniami, co spowodowałoby, że pomiary nadal opierałyby się na poprzednich
DpiScale
. Aplikacja, którą pisałem, była dla mnie, sama i nie mam takiej konfiguracji, więc nie miałem z czym testować, a kiedy zastosowałem się do wskazówek, skończyło się na aplikacji, która nie mogła się uruchomić z powodu manifestu błędy, więc zrezygnowałem, ale warto przyjrzeć się temu i dostosować manifest aplikacji, aby zawierał:<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>
Zgodnie z dokumentacją:
źródło
Znalazłem metody, które działają dobrze ...
/// <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); }
źródło
Rozwiązałem to, dodając ścieżkę powiązania do elementu w kodzie zaplecza:
<TextBlock x:Name="MyText" Width="{Binding Path=ActualWidth, ElementName=MyText}" />
Okazało się, że jest to o wiele czystsze rozwiązanie niż dodanie całego narzutu powyższych odwołań, takich jak FormattedText, do mojego kodu.
Potem udało mi się to zrobić:
double d_width = MyText.Width;
źródło
d_width = MyText.ActualWidth;
bez wiązania. Problem polega na tym, żeTextBlock
nie ma jeszcze drzewa wizualnego.Używam tego:
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)
źródło
Znalazłem to dla Ciebie:
Graphics g = control.CreateGraphics(); int width =(int)g.MeasureString(aString, control.Font).Width; g.dispose();
źródło