wpf mvvm font-size

wpf - Cómo escalar automáticamente el tamaño de fuente para un grupo de controles?



mvvm font-size (6)

Tengo algunos TextBlocks en WPF en una grilla que me gustaría escalar dependiendo de su ancho / altura disponible. Cuando busqué el tamaño de fuente de escalado automático, la sugerencia típica es colocar TextBlock en un ViewBox.

Entonces hice esto:

<Grid> <Grid.ColumnDefinitions> <ColumnDefinition Width="*" /> <ColumnDefinition Width="*" /> <ColumnDefinition Width="*" /> </Grid.ColumnDefinitions> <Viewbox MaxHeight="18" Grid.Column="0" Stretch="Uniform" Margin="5" HorizontalAlignment="Stretch" VerticalAlignment="Stretch"> <TextBlock Text="{Binding Text1}" /> </Viewbox> <Viewbox MaxHeight="18" Grid.Column="1" Stretch="Uniform" Margin="5" HorizontalAlignment="Stretch" VerticalAlignment="Stretch"> <TextBlock Text="{Binding Text2}" /> </Viewbox> <Viewbox MaxHeight="18" Grid.Column="2" Stretch="Uniform" Margin="5" HorizontalAlignment="Stretch" VerticalAlignment="Stretch"> <TextBlock Text="{Binding Text3}" /> </Viewbox> </Grid>

Y escala la fuente para cada TextBlock automáticamente. Sin embargo, esto parece divertido porque si uno de los TextBlocks tiene texto más largo, estará en una fuente más pequeña, mientras que los elementos de la cuadrícula vecinos estarán en una fuente más grande. Quiero que el tamaño de la Fuente se escale por grupo, quizás sería bueno si pudiera especificar un "SharedSizeGroup" para que un conjunto de controles dimensione automáticamente su fuente.

p.ej

El primer texto de bloques de texto podría ser "3/26/2013 10:45:30 AM", y el segundo texto de TextBlocks podría decir "FileName.ext". Si están en el ancho de una ventana, y el usuario comienza a cambiar el tamaño de la ventana cada vez más pequeña. La fecha comenzará a hacer que su fuente sea más pequeña que el nombre del archivo, dependiendo de la longitud del nombre del archivo.

Idealmente, una vez que uno de los campos de texto comienza a cambiar el tamaño del tamaño del punto de la fuente, todos coincidirán. ¿Alguien ha encontrado una solución para esto o puede darme una oportunidad de cómo lo haría funcionar? Si requiere un código personalizado, con suerte podremos volver a empaquetarlo en una Combinación o Comportamiento adjunto personalizado para que sea reutilizable en el futuro. Creo que es un problema bastante general, pero no pude encontrar nada buscando.

Actualización Probé la sugerencia de Mathieu y funciona, pero tiene algunos efectos secundarios:

<Window x:Class="WpfApplication6.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="MainWindow" Height="270" Width="522"> <Grid> <Grid.RowDefinitions> <RowDefinition Height="*"/> <RowDefinition Height="Auto" /> </Grid.RowDefinitions> <Rectangle Grid.Row="0" Fill="SkyBlue" /> <Viewbox Grid.Row="1" MaxHeight="30" Stretch="Uniform" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" > <Grid> <Grid.ColumnDefinitions> <ColumnDefinition Width="Auto" SharedSizeGroup="col"/> <ColumnDefinition Width="Auto" SharedSizeGroup="col"/> <ColumnDefinition Width="Auto" SharedSizeGroup="col"/> </Grid.ColumnDefinitions> <TextBlock Grid.Column="0" Text="SomeLongText" Margin="5" /> <TextBlock Grid.Column="1" Text="TextA" Margin="5" /> <TextBlock Grid.Column="2" Text="TextB" Margin="5" /> </Grid> </Viewbox> </Grid> </Window>

Honestamente, me faltan las columnas proporcionales. No me importaría AutoSizing las columnas para hacer un uso inteligente del espacio, pero tiene que abarcar todo el ancho de la ventana.

Aviso sin maxsize, en este ejemplo extendido el texto es demasiado grande:

<Window x:Class="WpfApplication6.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="MainWindow" Height="270" Width="522"> <Grid> <Grid.RowDefinitions> <RowDefinition Height="*"/> <RowDefinition Height="Auto" /> </Grid.RowDefinitions> <Rectangle Grid.Row="0" Fill="SkyBlue" /> <Viewbox Grid.Row="1" Stretch="Uniform" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" > <Grid> <Grid.ColumnDefinitions> <ColumnDefinition Width="Auto" SharedSizeGroup="col"/> <ColumnDefinition Width="Auto" SharedSizeGroup="col"/> <ColumnDefinition Width="Auto" SharedSizeGroup="col"/> </Grid.ColumnDefinitions> <TextBlock Grid.Column="0" Text="SomeLongText" Margin="5" /> <TextBlock Grid.Column="1" Text="TextA" Margin="5" /> <TextBlock Grid.Column="2" Text="TextB" Margin="5" /> </Grid> </Viewbox> </Grid>

Aquí, me gustaría limitar el tamaño de la fuente, para que no desperdicie el espacio de la ventana vertical. Estoy esperando que la salida se alinee a la izquierda, al centro y a la derecha con la fuente siendo lo más grande posible hasta el tamaño máximo deseado.

@adabyron

La solución que propones no está mal (y es la mejor hasta ahora) pero tiene algunas limitaciones. Por ejemplo, inicialmente quería que mis columnas fueran proporcionales (la segunda debe estar centrada). Por ejemplo, mis TextBlocks podrían etiquetar el inicio, el centro y la parada de un gráfico donde la alineación importa.

<Window x:Class="WpfApplication6.Window1" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity" xmlns:b="clr-namespace:WpfApplication6.Behavior" Title="MainWindow" Height="350" Width="525"> <Grid> <Grid.RowDefinitions> <RowDefinition Height="*"/> <RowDefinition Height="Auto" /> </Grid.RowDefinitions> <Rectangle Grid.Row="0" Fill="SkyBlue" /> <Line X1="0.5" X2="0.5" Y1="0" Y2="1" Stretch="Fill" StrokeThickness="3" Stroke="Red" /> <Grid Grid.Row="1"> <i:Interaction.Behaviors> <b:MoveToViewboxBehavior /> </i:Interaction.Behaviors> <Viewbox Stretch="Uniform" /> <ContentPresenter > <ContentPresenter.Content> <Grid x:Name="TextBlockContainer"> <Grid.Resources> <Style TargetType="TextBlock" > <Setter Property="FontSize" Value="16" /> <Setter Property="Margin" Value="5" /> </Style> </Grid.Resources> <Grid.ColumnDefinitions> <ColumnDefinition Width="*" /> <ColumnDefinition Width="*" /> <ColumnDefinition Width="*" /> <ColumnDefinition Width="*" /> <ColumnDefinition Width="*" /> </Grid.ColumnDefinitions> <TextBlock Grid.Column="0" Text="SomeLongText" VerticalAlignment="Center" HorizontalAlignment="Center" /> <TextBlock Grid.Column="2" Text="TextA" HorizontalAlignment="Center" VerticalAlignment="Center" /> <TextBlock Grid.Column="4" Text="TextB" HorizontalAlignment="Center" VerticalAlignment="Center" /> </Grid> </ContentPresenter.Content> </ContentPresenter> </Grid> </Grid> </Window>

Y aqui esta el resultado. Observe que no sabe que se está recortando desde el principio, y luego, cuando sustituye a ViewBox, parece que la cuadrícula se ajusta por defecto al tamaño de columna "Auto" y ya no se alinea en el centro.


Coloque su grilla en el ViewBox, que escalará toda la Grilla:

<Viewbox Stretch="Uniform" HorizontalAlignment="Stretch" VerticalAlignment="Stretch"> <Grid> <Grid.ColumnDefinitions> <ColumnDefinition Width="*" /> <ColumnDefinition Width="*" /> <ColumnDefinition Width="*" /> </Grid.ColumnDefinitions> <TextBlock Grid.Column="0" Text="{Binding Text1}" Margin="5" /> <TextBlock Grid.Column="1" Text="{Binding Text2}" Margin="5" /> <TextBlock Grid.Column="2" Text="{Binding Text3}" Margin="5" /> </Grid> </Viewbox>


Creo que sé el camino a seguir y te dejaré el resto. En este ejemplo, vinculo FontSize al ActualHeight del TextBlock, usando un convertidor (el convertidor está debajo):

<Window x:Class="MyNamespace.Test" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:Converters="clr-namespace:UpdateYeti.Converters" Title="Test" Height="570" Width="522"> <Grid Height="370" Width="522"> <Grid.Resources> <Converters:HeightToFontSizeConverter x:Key="conv" /> </Grid.Resources> <Grid.RowDefinitions> <RowDefinition Height="*"/> <RowDefinition Height="Auto" /> </Grid.RowDefinitions> <Rectangle Grid.Row="0" Fill="SkyBlue" /> <Grid Grid.Row="1" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" MinHeight="60" Background="Beige"> <Grid.ColumnDefinitions> <ColumnDefinition Width="Auto" /> <ColumnDefinition Width="*" /> <ColumnDefinition Width="Auto" /> </Grid.ColumnDefinitions> <TextBlock Grid.Column="0" Text="SomeLongText" Margin="5" FontSize="{Binding RelativeSource={RelativeSource Self}, Path=ActualHeight, Converter={StaticResource conv}}" /> <TextBlock Grid.Column="1" Text="TextA" Margin="5" HorizontalAlignment="Center" FontSize="{Binding RelativeSource={RelativeSource Self}, Path=ActualHeight, Converter={StaticResource conv}}" /> <TextBlock Grid.Column="2" Text="TextB" Margin="5" FontSize="{Binding RelativeSource={RelativeSource Self}, Path=ActualHeight, Converter={StaticResource conv}}" /> </Grid> </Grid> </Window> [ValueConversion(typeof(double), typeof(double))] class HeightToFontSizeConverter : IValueConverter { public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { // here you can use the parameter that you can give in here via setting , ConverterParameter=''something''} or use any nice login with the VisualTreeHelper to make a better return value, or maybe even just hardcode some max values if you like var height = (double)value; return .65 * height; } public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) { throw new NotImplementedException(); } }


Observación general: una posible alternativa al escalado de texto completo podría ser simplemente usar TextTrimming en los TextBlocks.

He luchado para encontrar una solución a esta. Usar una vista es muy difícil de mezclar con cualquier ajuste de diseño. Lo peor de todo es que ActualWidth, etc. no cambia dentro de una vista. Así que finalmente decidí usar el viewbox solo si era absolutamente necesario, que es cuando ocurriría el recorte. Por lo tanto, estoy moviendo el contenido entre un ContentPresenter y un Viewbox, dependiendo del espacio disponible.

Esta solución no es tan genérica como me gustaría, principalmente MoveToViewboxBehavior asume que está conectada a una grilla con la siguiente estructura. Si eso no puede ser acomodado, el comportamiento probablemente tendrá que ser ajustado. Crear un control de usuario y denotar las partes necesarias (PARTE _...) podría ser una alternativa válida.

Tenga en cuenta que he ampliado las columnas de la cuadrícula de tres a cinco, porque eso hace que la solución sea mucho más fácil. Significa que el bloque de texto del medio no estará exactamente en el medio, en el sentido de coordenadas absolutas, sino que está centrado entre los bloques de texto a la izquierda y a la derecha.

<Grid > <!-- MoveToViewboxBehavior attached to this grid --> <Viewbox /> <ContentPresenter> <ContentPresenter.Content> <Grid x:Name="TextBlockContainer"> <TextBlocks ... /> </Grid> </ContentPresenter.Content> </ContentPresenter> </Grid>

Xaml:

<Window x:Class="WpfApplication1.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity" xmlns:beh="clr-namespace:WpfApplication1.Behavior" Title="MainWindow" Height="350" Width="525"> <Grid> <Grid.RowDefinitions> <RowDefinition Height="*"/> <RowDefinition Height="Auto" /> </Grid.RowDefinitions> <Rectangle Grid.Row="0" Fill="SkyBlue" /> <Grid Grid.Row="1"> <i:Interaction.Behaviors> <beh:MoveToViewboxBehavior /> </i:Interaction.Behaviors> <Viewbox Stretch="Uniform" /> <ContentPresenter > <ContentPresenter.Content> <Grid x:Name="TextBlockContainer"> <Grid.Resources> <Style TargetType="TextBlock" > <Setter Property="FontSize" Value="16" /> <Setter Property="Margin" Value="5" /> </Style> </Grid.Resources> <Grid.ColumnDefinitions> <ColumnDefinition Width="Auto" /> <ColumnDefinition Width="*" /> <ColumnDefinition Width="Auto" /> <ColumnDefinition Width="*" /> <ColumnDefinition Width="Auto" /> </Grid.ColumnDefinitions> <TextBlock Grid.Column="0" Text="SomeLongText" /> <TextBlock Grid.Column="2" Text="TextA" /> <TextBlock Grid.Column="4" Text="TextB" /> </Grid> </ContentPresenter.Content> </ContentPresenter> </Grid> </Grid> </Window>

MoveToViewBoxBehavior:

using System; using System.Collections.Generic; using System.Globalization; using System.Windows; using System.Windows.Controls; using System.Windows.Interactivity; using System.Windows.Media; using WpfApplication1.Helpers; namespace WpfApplication1.Behavior { public class MoveToViewboxBehavior : Behavior<Grid> { // IsClipped public bool IsClipped { get { return (bool)GetValue(IsClippedProperty); } set { SetValue(IsClippedProperty, value); } } public static readonly DependencyProperty IsClippedProperty = DependencyProperty.Register("IsClipped", typeof(bool), typeof(MoveToViewboxBehavior), new PropertyMetadata(false, OnIsClippedChanged)); private static void OnIsClippedChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e) { var beh = (MoveToViewboxBehavior)sender; Grid grid = beh.AssociatedObject; Viewbox vb = VisualHelper.FindVisualChild<Viewbox>(grid); ContentPresenter cp = VisualHelper.FindVisualChild<ContentPresenter>(grid); if ((bool)e.NewValue) { // is clipped, so move content to Viewbox UIElement element = cp.Content as UIElement; cp.Content = null; vb.Child = element; } else { // can be shown without clipping, so move content to ContentPresenter cp.Content = vb.Child; vb.Child = null; } } protected override void OnAttached() { this.AssociatedObject.SizeChanged += (s, e) => { IsClipped = CalculateIsClipped(); }; } // Determines if the width of all textblocks within TextBlockContainer (using MaxFontSize) are wider than the AssociatedObject grid private bool CalculateIsClipped() { double totalDesiredWidth = 0d; Grid grid = VisualHelper.FindVisualChildByName<Grid>(this.AssociatedObject, "TextBlockContainer"); List<TextBlock> tbs = VisualHelper.FindVisualChildren<TextBlock>(grid); foreach (var tb in tbs) { if (tb.TextWrapping != TextWrapping.NoWrap) return false; totalDesiredWidth += MeasureText(tb).Width + tb.Margin.Left + tb.Margin.Right + tb.Padding.Left + tb.Padding.Right; } return Math.Round(this.AssociatedObject.ActualWidth, 5) < Math.Round(totalDesiredWidth, 5); } // Measures text size of textblock private Size MeasureText(TextBlock tb) { var formattedText = new FormattedText(tb.Text, CultureInfo.CurrentUICulture, FlowDirection.LeftToRight, new Typeface(tb.FontFamily, tb.FontStyle, tb.FontWeight, tb.FontStretch), tb.FontSize, Brushes.Black); return new Size(formattedText.Width, formattedText.Height); } } }

VisualHelper:

public static class VisualHelper { public static T FindVisualChild<T>(DependencyObject obj) where T : DependencyObject { T child = null; for (int i = 0; i < VisualTreeHelper.GetChildrenCount(obj); i++) { var o = VisualTreeHelper.GetChild(obj, i); if (o != null) { child = o as T; if (child != null) break; else { child = FindVisualChild<T>(o); // recursive if (child != null) break; } } } return child; } public static List<T> FindVisualChildren<T>(DependencyObject obj) where T : DependencyObject { List<T> children = new List<T>(); for (int i = 0; i < VisualTreeHelper.GetChildrenCount(obj); i++) { var o = VisualTreeHelper.GetChild(obj, i); if (o != null) { if (o is T) children.Add((T)o); children.AddRange(FindVisualChildren<T>(o)); // recursive } } return children; } public static T FindVisualChildByName<T>(DependencyObject parent, string name) where T : FrameworkElement { T child = default(T); for (int i = 0; i < VisualTreeHelper.GetChildrenCount(parent); i++) { var o = VisualTreeHelper.GetChild(parent, i); if (o != null) { child = o as T; if (child != null && child.Name == name) break; else child = FindVisualChildByName<T>(o, name); if (child != null) break; } } return child; } }


Puede usar ItemsControl oculto en un ViewBox.

<Grid> <Grid.ColumnDefinitions> <ColumnDefinition Width="*"/> <ColumnDefinition Width="*"/> <ColumnDefinition Width="*"/> </Grid.ColumnDefinitions> <Viewbox VerticalAlignment="Bottom"> <Grid> <TextBlock Text="SomeLongText"/> <ItemsControl Visibility="Hidden"> <ItemsPanelTemplate> <Grid/> </ItemsPanelTemplate> <TextBlock Text="SomeLongText"/> <TextBlock Text="TextA"/> <TextBlock Text="TextB"/> </ItemsControl> </Grid> </Viewbox> <Viewbox Grid.Column="1" VerticalAlignment="Bottom"> <Grid> <TextBlock Text="TextA"/> <ItemsControl Visibility="Hidden"> <ItemsPanelTemplate> <Grid/> </ItemsPanelTemplate> <TextBlock Text="SomeLongText"/> <TextBlock Text="TextA"/> <TextBlock Text="TextB"/> </ItemsControl> </Grid> </Viewbox> <Viewbox Grid.Column="2" VerticalAlignment="Bottom"> <Grid> <TextBlock Text="TextB"/> <ItemsControl Visibility="Hidden"> <ItemsPanelTemplate> <Grid/> </ItemsPanelTemplate> <TextBlock Text="SomeLongText"/> <TextBlock Text="TextA"/> <TextBlock Text="TextB"/> </ItemsControl> </Grid> </Viewbox> </Grid>

o

<Grid> <Grid.ColumnDefinitions> <ColumnDefinition Width="*"/> <ColumnDefinition Width="*"/> <ColumnDefinition Width="*"/> </Grid.ColumnDefinitions> <Viewbox VerticalAlignment="Bottom"> <Grid> <TextBlock Text="{Binding Text1}"/> <ItemsControl Visibility="Hidden" ItemsSource="{Binding AllText}"> <ItemsPanelTemplate> <Grid/> </ItemsPanelTemplate> <ItemsControl.ItemTemplate> <DataTemplate> <TextBlock Text="{Binding}"/> </DataTemplate> </ItemsControl.ItemTemplate> </ItemsControl> </Grid> </Viewbox> <Viewbox Grid.Column="1" VerticalAlignment="Bottom"> <Grid> <TextBlock Text="{Binding Text2}"/> <ItemsControl Visibility="Hidden" ItemsSource="{Binding AllText}"> <ItemsPanelTemplate> <Grid/> </ItemsPanelTemplate> <ItemsControl.ItemTemplate> <DataTemplate> <TextBlock Text="{Binding}"/> </DataTemplate> </ItemsControl.ItemTemplate> </ItemsControl> </Grid> </Viewbox> <Viewbox Grid.Column="2" VerticalAlignment="Bottom"> <Grid> <TextBlock Text="{Binding Text3}"/> <ItemsControl Visibility="Hidden" ItemsSource="{Binding AllText}"> <ItemsPanelTemplate> <Grid/> </ItemsPanelTemplate> <ItemsControl.ItemTemplate> <DataTemplate> <TextBlock Text="{Binding}"/> </DataTemplate> </ItemsControl.ItemTemplate> </ItemsControl> </Grid> </Viewbox> </Grid>


Quería editar la respuesta que ya había ofrecido, pero luego decidí que tenía más sentido publicar una nueva, porque realmente depende de los requisitos cuál prefiero. Esto aquí probablemente se ajusta mejor a la idea de Alan, porque

  • El bloque de texto del medio permanece en el medio de la ventana
  • Ajuste de tamaño de fuente debido al recorte de altura se acomoda
  • Bastante más genérico
  • No hay viewbox involucrado

El otro tiene la ventaja de

  • El espacio para los bloques de texto se asigna de manera más eficiente (sin márgenes innecesarios)
  • Textblocks puede tener diferentes tamaños de letra

Probé esta solución también en un contenedor superior de tipo StackPanel / DockPanel, me porté decentemente.

Tenga en cuenta que jugando con los anchos / alturas de columnas / filas (auto / estrellas), puede obtener diferentes comportamientos. Por lo tanto, también sería posible tener las tres columnas de bloque de texto en forma de estrellas, pero eso significa que el recorte de ancho ocurre antes y hay más margen. O si la fila en la que se encuentra la rejilla es de tamaño automático, nunca se producirá recorte de altura.

Xaml:

<Window x:Class="WpfApplication1.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity" xmlns:beh="clr-namespace:WpfApplication1.Behavior" Title="MainWindow" Height="350" Width="525"> <Grid> <Grid.RowDefinitions> <RowDefinition Height="0.9*"/> <RowDefinition Height="0.1*" /> </Grid.RowDefinitions> <Rectangle Fill="DarkOrange" /> <Grid x:Name="TextBlockContainer" Grid.Row="1" > <i:Interaction.Behaviors> <beh:ScaleFontBehavior MaxFontSize="32" /> </i:Interaction.Behaviors> <Grid.Resources> <Style TargetType="TextBlock" > <Setter Property="Margin" Value="5" /> <Setter Property="VerticalAlignment" Value="Center" /> </Style> </Grid.Resources> <Grid.ColumnDefinitions> <ColumnDefinition Width="*" /> <ColumnDefinition Width="Auto" /> <ColumnDefinition Width="*" /> </Grid.ColumnDefinitions> <TextBlock Grid.Column="0" Text="SomeLongText" /> <TextBlock Grid.Column="1" Text="TextA" HorizontalAlignment="Center" /> <TextBlock Grid.Column="2" Text="TextB" HorizontalAlignment="Right" /> </Grid> </Grid> </Window>

ScaleFontBehavior:

using System; using System.Collections.Generic; using System.Globalization; using System.Windows; using System.Windows.Controls; using System.Windows.Interactivity; using System.Windows.Media; using WpfApplication1.Helpers; namespace WpfApplication1.Behavior { public class ScaleFontBehavior : Behavior<Grid> { // MaxFontSize public double MaxFontSize { get { return (double)GetValue(MaxFontSizeProperty); } set { SetValue(MaxFontSizeProperty, value); } } public static readonly DependencyProperty MaxFontSizeProperty = DependencyProperty.Register("MaxFontSize", typeof(double), typeof(ScaleFontBehavior), new PropertyMetadata(20d)); protected override void OnAttached() { this.AssociatedObject.SizeChanged += (s, e) => { CalculateFontSize(); }; } private void CalculateFontSize() { double fontSize = this.MaxFontSize; List<TextBlock> tbs = VisualHelper.FindVisualChildren<TextBlock>(this.AssociatedObject); // get grid height (if limited) double gridHeight = double.MaxValue; Grid parentGrid = VisualHelper.FindUpVisualTree<Grid>(this.AssociatedObject.Parent); if (parentGrid != null) { RowDefinition row = parentGrid.RowDefinitions[Grid.GetRow(this.AssociatedObject)]; gridHeight = row.Height == GridLength.Auto ? double.MaxValue : this.AssociatedObject.ActualHeight; } foreach (var tb in tbs) { // get desired size with fontsize = MaxFontSize Size desiredSize = MeasureText(tb); double widthMargins = tb.Margin.Left + tb.Margin.Right; double heightMargins = tb.Margin.Top + tb.Margin.Bottom; double desiredHeight = desiredSize.Height + heightMargins; double desiredWidth = desiredSize.Width + widthMargins; // adjust fontsize if text would be clipped vertically if (gridHeight < desiredHeight) { double factor = (desiredHeight - heightMargins) / (this.AssociatedObject.ActualHeight - heightMargins); fontSize = Math.Min(fontSize, MaxFontSize / factor); } // get column width (if limited) ColumnDefinition col = this.AssociatedObject.ColumnDefinitions[Grid.GetColumn(tb)]; double colWidth = col.Width == GridLength.Auto ? double.MaxValue : col.ActualWidth; // adjust fontsize if text would be clipped horizontally if (colWidth < desiredWidth) { double factor = (desiredWidth - widthMargins) / (col.ActualWidth - widthMargins); fontSize = Math.Min(fontSize, MaxFontSize / factor); } } // apply fontsize (always equal fontsizes) foreach (var tb in tbs) { tb.FontSize = fontSize; } } // Measures text size of textblock private Size MeasureText(TextBlock tb) { var formattedText = new FormattedText(tb.Text, CultureInfo.CurrentUICulture, FlowDirection.LeftToRight, new Typeface(tb.FontFamily, tb.FontStyle, tb.FontWeight, tb.FontStretch), this.MaxFontSize, Brushes.Black); // always uses MaxFontSize for desiredSize return new Size(formattedText.Width, formattedText.Height); } } }

VisualHelper:

public static List<T> FindVisualChildren<T>(DependencyObject obj) where T : DependencyObject { List<T> children = new List<T>(); for (int i = 0; i < VisualTreeHelper.GetChildrenCount(obj); i++) { var o = VisualTreeHelper.GetChild(obj, i); if (o != null) { if (o is T) children.Add((T)o); children.AddRange(FindVisualChildren<T>(o)); // recursive } } return children; } public static T FindUpVisualTree<T>(DependencyObject initial) where T : DependencyObject { DependencyObject current = initial; while (current != null && current.GetType() != typeof(T)) { current = VisualTreeHelper.GetParent(current); } return current as T; }


Una solución podría ser algo como eso:

Elija un MaxFontSize, luego defina el FontSize apropiado que se mostrará teniendo en cuenta la ventana actual mediante el uso de una ecuación lineal. La altura o ancho de la ventana limitaría la elección final de FontSize.

Tomemos el caso de un "TextBlock de tipo único" para toda la grilla:

Window.Current.SizeChanged += (sender, args) => { int minFontSize = a; int maxFontSize = b; int maxMinFontSizeDiff = maxFontSize - minFontSize; int gridMinHeight = c; int gridMaxHeight = d; int gridMaxMinHeightDiff = gridMaxHeight - gridMinHeight; int gridMinWidth = e; int gridMaxWidth = f; int gridMaxMinHeightDiff = gridMaxWidth - gridMaxWidth; //Linear equation considering "max/min FontSize" and "max/min GridHeight/GridWidth" double heightFontSizeDouble = (maxMinFontSizeDiff / gridMaxMinHeightDiff ) * Grid.ActualHeight + (maxFontSize - (gridMaxHeight * (maxMinFontSizeDiff / gridMaxMinHeightDiff))) double widthFontSizeDouble = (maxMinFontSizeDiff / gridMaxMinWidthDiff ) * Grid.ActualWidth + (maxFontSize - (gridMaxWidth * (maxMinFontSizeDiff / gridMaxMinWidthDiff))) int heightFontSize = (int)Math.Round(heightFontSizeDouble) int widthFontSize = (int)Math.Round(widthFontSizeDouble) foreach (var children in Grid.Children) { (children as TextBlock).FontSize = Math.Min(heightFontSize, widthFontSize); } }