Digamos que tengo TextBlock
con el texto "Some Text" y el tamaño de fuente 10.0 .
¿Cómo puedo calcular el TextBlock
ancho apropiado ?
Digamos que tengo TextBlock
con el texto "Some Text" y el tamaño de fuente 10.0 .
¿Cómo puedo calcular el TextBlock
ancho apropiado ?
ActualWidth
.
Respuestas:
Usa la FormattedText
clase.
Hice una función auxiliar en mi código:
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);
}
Devuelve píxeles independientes del dispositivo que se pueden usar en el diseño de WPF.
Para el registro ... supongo que el operador está tratando de determinar programáticamente el ancho que tomará el bloque de texto después de ser agregado al árbol visual. En mi opinión, una solución mejor que formattedText (¿cómo se maneja algo como textWrapping?) Sería usar Medir y Organizar en un TextBlock de muestra. p.ej
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
La solución proporcionada era apropiada para .Net Framework 4.5, sin embargo, con el escalado de DPI de Windows 10 y Framework 4.6.x agregando diversos grados de compatibilidad, el constructor utilizado para medir el texto ahora está marcado [Obsolete]
, junto con cualquier constructor de ese método que lo haga. no incluye el pixelsPerDip
parámetro.
Desafortunadamente, es un poco más complicado, pero dará como resultado una mayor precisión con las nuevas capacidades de escalado.
### PixelsPerDip
Según MSDN, esto representa:
El valor de píxeles independientes por densidad de píxeles, que es el equivalente al factor de escala. Por ejemplo, si el DPI de una pantalla es 120 (o 1,25 porque 120/96 = 1,25), se dibuja 1,25 píxeles por píxel independiente de la densidad. DIP es la unidad de medida utilizada por WPF para ser independiente de la resolución del dispositivo y de los DPI.
Aquí está mi implementación de la respuesta seleccionada basada en la guía de Microsoft / WPF-Samples repositorio de GitHub de con reconocimiento de escala de DPI.
Se requiere una configuración adicional para admitir completamente el escalado de DPI a partir de Windows 10 Anniversary (debajo del código), que no pude hacer funcionar, pero sin él, esto funciona en un solo monitor con el escalado configurado (y respeta los cambios de escala). El documento de Word en el repositorio anterior es la fuente de esa información ya que mi aplicación no se iniciaría una vez que agregué esos valores. Este código de muestra del mismo repositorio también sirvió como un buen punto de referencia.
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
}
}
De acuerdo con la documentación en Microsoft / WPF-Samples, debe agregar algunas configuraciones al manifiesto de la aplicación para cubrir la capacidad de Windows 10 Anniversary de tener diferentes configuraciones de DPI por pantalla en configuraciones de múltiples monitores. Es una suposición justa que sin estas configuraciones, el evento OnDpiChanged podría no aparecer cuando una ventana se mueve de una pantalla a otra con configuraciones diferentes, lo que haría que sus medidas siguieran dependiendo de las anteriores DpiScale
. La aplicación que estaba escribiendo era para mí, solo, y no tengo ese tipo de configuración, así que no tenía nada con qué probar y cuando seguí las instrucciones, terminé con una aplicación que no se iniciaba debido al manifiesto. errores, así que me di por vencido, pero sería una buena idea revisarlo y ajustar el manifiesto de la aplicación para que contenga:
<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>
Según la documentación:
La combinación de [estas] dos etiquetas tiene el siguiente efecto: 1) Por monitor para> = Actualización de aniversario de Windows 10 2) Sistema <Actualización de aniversario de Windows 10
Encontré algunos métodos que funcionan bien ...
/// <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);
}
Resolví esto agregando una ruta de enlace al elemento en el código de backend:
<TextBlock x:Name="MyText" Width="{Binding Path=ActualWidth, ElementName=MyText}" />
Encontré que esta es una solución mucho más limpia que agregar toda la sobrecarga de las referencias anteriores como FormattedText a mi código.
Después, pude hacer esto:
double d_width = MyText.Width;
d_width = MyText.ActualWidth;
sin atar. El problema es cuando aún TextBlock
no está en el árbol visual.
Yo uso este:
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)