web-dev-qa-db-ja.com

コントロールのグループのフォントサイズを自動的に拡大縮小する方法は?

使用可能な幅/高さに応じてスケーリングしたいグリッドのWPFにいくつかのTextBlocksがあります。フォントサイズの自動スケーリングを検索したときの典型的な提案は、TextBlockをViewBoxに入れることです。

だから私はこれをやった:

<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>

また、各TextBlockのフォントを自動的にスケーリングします。ただし、TextBlocksの1つが長いテキストを持っている場合、隣接するグリッド要素が大きいフォントになる一方で小さいフォントになるため、これはおかしいように見えます。フォントサイズをグループごとにスケーリングする必要があります。おそらく、コントロールのセットに "SharedSizeGroup"を指定してフォントのサイズを自動調整できると便利です。

例えば.

最初のテキストブロックテキストは「3/26/2013 10:45:30 AM」であり、2番目のテキストブロックテキストは「FileName.ext」である可能性があります。これらがウィンドウの幅を横切っており、ユーザーがウィンドウのサイズを徐々に小さくし始めた場合。日付は、ファイル名の長さに応じて、フォントをファイル名よりも小さくし始めます。

理想的には、テキストフィールドの1つがフォントポイントサイズのサイズ変更を開始すると、それらはすべて一致します。誰かがこのための解決策を考え出しましたか、それをどのように機能させるかを試してみてください。カスタムコードが必要な場合は、将来的に再利用できるように、カスタムのBlendまたはAttached Behaviorに再パッケージ化できれば幸いです。これはかなり一般的な問題だと思いますが、検索しても何も見つかりませんでした。


更新 Mathieuの提案を試してみましたが、これは一種の動作をしますが、いくつかの副作用があります:

<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>

Side-Effects

正直なところ、プロポーショナル列がなくても大丈夫でしょう。スペースを有効に使用するために列のサイズを自動調整してもかまいませんが、ウィンドウの幅全体に広がる必要があります。

Maxsizeがないことに注意してください。この拡張例では、テキストが大きすぎます。

<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>

Text too large without MaxSize

ここでは、フォントが取得できる大きさを制限したいので、垂直ウィンドウの領域を無駄にしません。 出力は、希望する最大サイズまで可能な限り大きいフォントで、左、中央、および右に揃えられると予想しています。


@ adabyron

あなたが提案する解決策は悪くありません(そして、まだ最高です)が、いくつかの制限があります。たとえば、最初は列を比例させたいと思っていました(2番目の列は中央に配置する必要があります)。たとえば、TextBlocksはグラフの開始、中央、停止の位置合わせが重要な場所にラベルを付ける場合があります。

<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>

そして、これが結果です。早い段階でクリップされていることを認識していないことに注意してください。ViewBoxを置き換えると、Gridがデフォルトで列サイズ「Auto」になり、中心を揃えないように見えます。

Scaling with adabyron's suggestion

44
Alan

私はすでに提供した回答を編集したかったのですが、私が好む要件に本当に依存するため、新しい回答を投稿する方が理にかなっています。これはおそらくAlanの考えによく合っているでしょう。なぜなら、

  • 中央のテキストブロックはウィンドウの中央にとどまります
  • 高さのクリッピングによるフォントサイズの調整に対応
  • かなり一般的
  • ビューボックスは含まれません

enter image description here

enter image description here

他の1つ には次の利点があります。

  • テキストブロックのスペースがより効率的に割り当てられます(不要なマージンなし)
  • テキストブロックのフォントサイズは異なる場合があります

StackPanel/DockPanelタイプの最上位コンテナでもこのソリューションをテストし、適切に動作しました。

列/行の幅/高さ(自動/スター付き)をいじってみると、さまざまな動作が得られることに注意してください。したがって、3つのテキストブロック列すべてをスター化することも可能ですが、それは幅のクリッピングが早く発生し、マージンが増えることを意味します。または、グリッドが存在する行が自動サイズ設定されている場合、高さのクリッピングは発生しません。

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;
}
30
Mike Fuchs

グリッドをViewBoxに配置すると、Grid全体がスケーリングされます。

<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>
9
mathieu

私は行く方法を知っており、残りはあなたに任せると思います。この例では、コンバーターを使用してFontSizeをTextBlockのActualHeightにバインドしました(コンバーターは以下にあります)。

<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();
    }
}
1
Akku

ViewBoxで非表示のItemsControlを使用できます。

<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>

または

<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>
1
Tempeck

一般的な注意:テキストスケーリング全体の可能な代替手段は、TextBlocksでTextTrimmingを使用することです。

これに対する解決策を見つけるのに苦労しました。ビューボックスを使用することは、レイアウトの調整と組み合わせることが非常に困難です。最悪なのは、ActualWidthなどがビューボックス内で変化しないことです。そこで、どうしても必要な場合にのみ、つまりクリッピングが発生する場合にのみ、ビューボックスを使用することにしました。したがって、使用可能なスペースに応じて、ContentPresenterとViewboxの間でコンテンツを移動しています。


enter image description here

enter image description here


このソリューションは、私が望むほど一般的ではありません。主にMoveToViewboxBehaviorは、次の構造を持つグリッドに接続されていると想定しています。それに対応できない場合、ほとんどの場合、動作を調整する必要があります。ユーザーコントロールを作成し、必要な部分(PART _...)を示すことは、有効な代替手段です。

グリッドの列を3から5に拡張したことに注意してください。これにより、ソリューションが非常に簡単になります。つまり、真ん中のテキストブロックは、絶対座標の意味で正確に真ん中にはなく、代わりに左右のテキストブロックの中央に配置されます。

<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;
    }
}
1
Mike Fuchs

解決策は次のようになります。

MaxFontSizeを選択し、線形方程式を使用して、現在のウィンドウを考慮して表示される適切なFontSizeを定義します。ウィンドウの高さまたは幅は、最終的なFontSizeの選択を制限します。

グリッド全体の「単一の種類のTextBlock」の場合を考えてみましょう。

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);
            }
        }
0
Javert