テキストが1行に収まらなくなったときに、自動的に垂直方向にサイズ変更するようにTextBox
コントロールを構成するにはどうすればよいですか?
たとえば、次のXAMLでは次のようになります。
<DockPanel LastChildFill="True" Margin="0,0,0,0">
<Border Name="dataGridHeader"
DataContext="{Binding Descriptor.Filter}"
DockPanel.Dock="Top"
BorderThickness="1"
Style="{StaticResource ChamelionBorder}">
<Border
Padding="5"
BorderThickness="1,1,0,0"
BorderBrush="{DynamicResource {ComponentResourceKey TypeInTargetAssembly=dc:NavigationPane,
ResourceId={x:Static dc:NavigationPaneColors.NavPaneTitleBorder}}}">
<StackPanel Orientation="Horizontal">
<TextBlock
Name="DataGridTitle"
FontSize="14"
FontWeight="Bold"
Foreground="{DynamicResource {ComponentResourceKey
TypeInTargetAssembly=dc:NavigationPane,
ResourceId={x:Static dc:NavigationPaneColors.NavPaneTitleForeground}}}"/>
<StackPanel Margin="5,0" Orientation="Horizontal"
Visibility="{Binding IsFilterEnabled, FallbackValue=Collapsed, Mode=OneWay, Converter={StaticResource BooleanToVisibility}}"
IsEnabled="{Binding IsFilterEnabled, FallbackValue=false}" >
<TextBlock />
<TextBox
Name="VerticallyExpandMe"
Padding="0, 0, 0, 0"
Margin="10,2,10,-1"
AcceptsReturn="True"
VerticalAlignment="Center"
Text="{Binding QueryString}"
Foreground="{DynamicResource {ComponentResourceKey
TypeInTargetAssembly=dc:NavigationPane,
ResourceId={x:Static dc:NavigationPaneColors.NavPaneTitleForeground}}}">
</TextBox>
</StackPanel>
</StackPanel>
</Border>
</Border>
</DockPanel>
「VerticallyExpandMe」という名前のTextBox
コントロールは、バインドされたテキストが1行に収まらない場合、自動的に垂直方向に展開する必要があります。 AcceptsReturn
をtrueに設定すると、TextBox
は、その中でEnterキーを押すと垂直方向に展開しますが、これを自動的に実行したいと思います。
Andre Luusの提案は基本的に正しいですが、レイアウトによってテキストの折り返しが無効になるため、実際にはここでは機能しません。その理由を説明します。
基本的に、問題は次のとおりです。テキストの折り返しは、要素の幅が制約されている場合にのみ何も行いませんが、TextBox
は、水平方向のStackPanel
の子孫であるため、制約のない幅になります。 (まあ、2つの水平スタックパネル。例を挙げたコンテキストによっては、おそらくもっと多くなります。)幅に制約がないため、TextBox
はいつ折り返しを開始するかわからないため、折り返しが発生することはありません。ラッピングを有効にした場合。 2つのことを行う必要があります。幅を制限することと、折り返しを有効にすることです。
これがより詳細な説明です。
あなたの例には、問題に関係のない多くの詳細が含まれています。これは、何が問題なのかを説明しやすくするために、いくらかトリミングしたバージョンです。
<StackPanel Orientation="Horizontal">
<TextBlock Name="DataGridTitle" />
<StackPanel
Margin="5,0"
Orientation="Horizontal"
>
<TextBlock />
<TextBox
Name="VerticallyExpandMe"
Margin="10,2,10,-1"
AcceptsReturn="True"
VerticalAlignment="Center"
Text="{Binding QueryString}"
>
</TextBox>
</StackPanel>
</StackPanel>
そこで、含まれているDockPanel
とその中にネストされた2つのBorder
要素を削除しました。これらは問題の一部ではなく、解決策にも関連していないためです。だから私はあなたの例のネストされたStackPanel
要素のペアから始めています。また、ほとんどの属性はレイアウトに関連していないため、ほとんどの属性も削除しました。
これは少し奇妙に見えます-このように2つのネストされた水平スタックパネルがあると冗長に見えますが、実行時にネストされたものを表示または非表示にする必要がある場合は、元のパネルでは実際に意味があります。ただし、問題がわかりやすくなります。
(空のTextBlock
タグも奇妙ですが、それは元のタグとまったく同じです。それは何の役にも立たないようです。)
そして、ここに問題があります:あなたのTextBox
はいくつかの水平StackPanel
要素の内側にあります、つまりその幅は制約されていません-あなたはテキストボックスに、実際に利用可能なスペースの量に関係なく、任意の幅に自由に拡張できると誤って伝えました。
StackPanel
は常に、スタッキングの方向に制約のないレイアウトを実行します。したがって、そのTextBox
をレイアウトする場合、水平サイズdouble.PositiveInfinity
をTextBox
に渡します。したがって、TextBox
は常に、必要以上のスペースがあると見なします。さらに、StackPanel
の子が実際に使用可能なスペースよりも多くのスペースを要求すると、StackPanel
は嘘をつき、そのスペースを与えるふりをしますが、それをトリミングします。
(これは、StackPanel
の極端な単純さに対して支払う代償です-実際には適合しないレイアウトを喜んで構築するため、骨の折れるほど単純です。StackPanel
を使用するのは、どちらかが本当に無制限である場合のみにしてください。 ScrollViewer
内にいるため、またはスペースが不足しないようにアイテムが十分に少ないと確信している場合、またはアイテムが取得されたときにパネルの端からアイテムが実行されることを気にしない場合は、スペース大きすぎて、レイアウトシステムが単にコンテンツをトリミングするよりも賢いことをしようとしないようにします。)
したがって、テキストの折り返しをオンにしても、ここでは役に立ちません。StackPanel
は常に、テキストに十分なスペースがあるふりをするからです。
別のレイアウト構造が必要です。スタックパネルは、テキストの折り返しを開始するために必要なレイアウトの制約を課さないため、使用するのは間違っています。
これはあなたが望むことを大まかに行う簡単な例です:
<Grid VerticalAlignment="Top">
<DockPanel>
<TextBlock
x:Name="DataGridTitle"
VerticalAlignment="Top"
DockPanel.Dock="Left"
/>
<TextBox
Name="VerticallyExpandMe"
AcceptsReturn="True"
TextWrapping="Wrap"
Text="{Binding QueryString}"
>
</TextBox>
</DockPanel>
</Grid>
まったく新しいWPFアプリケーションを作成し、それをメインウィンドウのコンテンツとして貼り付けると、必要な処理が実行されるはずです。TextBox
は、高さが1行から始まり、使用可能な幅を埋めます。テキストを入力すると、テキストを追加すると、一度に1行ずつ大きくなります。
もちろん、レイアウトの動作は常にコンテキストに敏感であるため、既存のアプリケーションの真ん中にそれを投げ込むだけでは不十分な場合があります。これは、固定サイズのスペース(ウィンドウの本体など)に貼り付けると機能しますが、幅に制約がないコンテキストに貼り付けると正しく機能しません。 (たとえば、ScrollViewer
の内部、または水平のStackPanel
の内部。)
したがって、これがうまくいかない場合は、レイアウトの他の場所で他の問題が発生している可能性があります。おそらく、他の場所でさらに多くのStackPanel
要素があります。あなたの例の外観から、レイアウトに本当に必要なものを考えてそれを単純化するのに時間を費やす価値があるでしょう-負のマージンの存在、そしてその空のTextBlock
のようなことをしていないように見える要素は通常過度に複雑なレイアウト。また、レイアウトが不必要に複雑になると、探している効果を実現するのが非常に難しくなります。
または、TextBlock
のWidth
を親のActualWidth
にバインドすることで、次のように制約することもできます。
<TextBlock Width="{Binding ElementName=*ParentElement*, Path=ActualWidth}"
Height="Auto" />
これにより、高さのサイズも自動的に変更されます。
MaxWidth
とTextWrapping="WrapWithOverflow"
を使用します。
TextBlockを拡張するこのクラスを使用できます。自動縮小を行い、MaxHeight/MaxWidthを考慮に入れます。
public class TextBlockAutoShrink : TextBlock
{
private double _defaultMargin = 6;
private Typeface _typeface;
static TextBlockAutoShrink()
{
TextBlock.TextProperty.OverrideMetadata(typeof(TextBlockAutoShrink), new FrameworkPropertyMetadata(new PropertyChangedCallback(TextPropertyChanged)));
}
public TextBlockAutoShrink() : base()
{
_typeface = new Typeface(this.FontFamily, this.FontStyle, this.FontWeight, this.FontStretch, this.FontFamily);
base.DataContextChanged += new DependencyPropertyChangedEventHandler(TextBlockAutoShrink_DataContextChanged);
}
private static void TextPropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs args)
{
var t = sender as TextBlockAutoShrink;
if (t != null)
{
t.FitSize();
}
}
void TextBlockAutoShrink_DataContextChanged(object sender, DependencyPropertyChangedEventArgs e)
{
FitSize();
}
protected override void OnRenderSizeChanged(SizeChangedInfo sizeInfo)
{
FitSize();
base.OnRenderSizeChanged(sizeInfo);
}
private void FitSize()
{
FrameworkElement parent = this.Parent as FrameworkElement;
if (parent != null)
{
var targetWidthSize = this.FontSize;
var targetHeightSize = this.FontSize;
var maxWidth = double.IsInfinity(this.MaxWidth) ? parent.ActualWidth : this.MaxWidth;
var maxHeight = double.IsInfinity(this.MaxHeight) ? parent.ActualHeight : this.MaxHeight;
if (this.ActualWidth > maxWidth)
{
targetWidthSize = (double)(this.FontSize * (maxWidth / (this.ActualWidth + _defaultMargin)));
}
if (this.ActualHeight > maxHeight)
{
var ratio = maxHeight / (this.ActualHeight);
// Normalize due to Height miscalculation. We do it step by step repeatedly until the requested height is reached. Once the fontsize is changed, this event is re-raised
// And the ActualHeight is lowered a bit more until it doesnt enter the enclosing If block.
ratio = (1 - ratio > 0.04) ? Math.Sqrt(ratio) : ratio;
targetHeightSize = (double)(this.FontSize * ratio);
}
this.FontSize = Math.Min(targetWidthSize, targetHeightSize);
}
}
}
ドキュメントのレイアウトを変更しないようにする別の簡単なアプローチを使用しています。
主なアイデアは、変更を開始する前にコントロールWidth
を設定しないことです。 TextBox
esの場合、SizeChanged
イベントを処理します。
<TextBox TextWrapping="Wrap" SizeChanged="TextBox_SizeChanged" />
private void TextBox_SizeChanged(object sender, SizeChangedEventArgs e)
{
FrameworkElement box = (FrameworkElement)sender;
if (e.PreviousSize.Width == 0 || box.Width < e.PreviousSize.Width)
return;
box.Width = e.PreviousSize.Width;
}