グリッド内の WPF に TextBlock がいくつかあり、利用可能な幅と高さに応じて拡大縮小したいと考えています。フォント サイズを自動的に拡大縮小する方法を検索したところ、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 のフォントは自動的に拡大縮小されます。ただし、TextBlock の 1 つに長いテキストがある場合、そのテキストは小さいフォントで表示され、隣接するグリッド要素は大きいフォントで表示されるため、これは奇妙に見えます。フォント サイズをグループごとに拡大縮小したいのですが、コントロールのセットに "SharedSizeGroup" を指定してフォント サイズを自動変更できれば便利かもしれません。
例えば
最初のテキスト ブロックのテキストは「3/26/2013 10:45:30 AM」で、2 番目の TextBlocks のテキストは「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>
正直に言うと、比例列がなくてもおそらく問題ありません。スペースを賢く利用するために列のサイズを自動調整しても構いませんが、ウィンドウの幅全体に広がる必要があります。
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>
ここでは、垂直方向のウィンドウ領域を無駄にしないように、フォントの大きさを制限したいと思います。出力は左、中央、右に揃えられ、フォントは希望する最大サイズまで可能な限り大きくなることを期待しています。
アダビロン
あなたが提案する解決策は悪くありません(そしてこれまでで最高のものです)が、いくつかの制限があります。たとえば、最初は列を比例させたいと思っていました(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 を置き換えると、グリッドがデフォルトで列サイズ「自動」に設定され、中央揃えされなくなったように見えます。
ベストアンサー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>
スケールフォント動作:
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);
}
}
}
ビジュアルヘルパー:
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;
}