Wpf 当文本不再适合一行时,如何将TextBox控件配置为自动垂直调整其大小?
当文本不再适合一行时,如何配置Wpf 当文本不再适合一行时,如何将TextBox控件配置为自动垂直调整其大小?,wpf,textbox,autosize,Wpf,Textbox,Autosize,当文本不再适合一行时,如何配置TextBox控件以自动垂直调整其大小 例如,在以下XAML中: 名为“VerticallyExpandMe”的文本框控件需要在绑定到它的文本不能在一行中显示时自动垂直展开。当AcceptsReturn设置为true时,TextBox如果我在其中按enter键,则会垂直展开,但我希望它自动展开。虽然Andre Luus的建议基本正确,但它实际上不会在这里工作,因为您的布局会破坏文本包装。我会解释原因的 从根本上说,问题在于:文本换行仅在元素的宽度受到约束时才执行
TextBox
控件以自动垂直调整其大小
例如,在以下XAML中:
名为“VerticallyExpandMe”的
文本框
控件需要在绑定到它的文本不能在一行中显示时自动垂直展开。当AcceptsReturn
设置为true时,TextBox
如果我在其中按enter键,则会垂直展开,但我希望它自动展开。虽然Andre Luus的建议基本正确,但它实际上不会在这里工作,因为您的布局会破坏文本包装。我会解释原因的
从根本上说,问题在于:文本换行仅在元素的宽度受到约束时才执行任何操作,但是文本框
的宽度不受约束,因为它是水平堆栈面板
的后代。(嗯,两个水平堆栈面板。可能更多,取决于您的示例所处的上下文。)由于宽度不受限制,文本框
不知道何时开始换行,因此即使启用换行,它也不会换行。您需要做两件事:约束其宽度和启用环绕
这里有更详细的解释
你的例子包含了很多与问题无关的细节。这里有一个版本,我稍微删减了一下,以便更容易解释错误:
<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>
如果您创建一个全新的WPF应用程序并将其粘贴到主窗口的内容中,您应该会发现它做了您想做的事情——TextBox
开始时有一行高,填充了可用的宽度,如果您键入文本,它会随着您添加更多文本一次增加一行
当然,布局行为总是对上下文敏感的,所以仅仅将其放到现有应用程序的中间可能是不够的。如果将其粘贴到固定大小的空间(例如,作为窗体)中,则该功能将正常工作,但如果将其粘贴到宽度不受限制的上下文中,则该功能将无法正常工作。(例如,在滚动查看器
内,或在水平堆叠面板
内)
因此,如果这对您不起作用,可能是因为布局中的其他地方出现了错误-可能还有更多的
StackPanel
元素。从您的示例来看,花一些时间思考布局中真正需要的内容并加以简化可能是值得的——负边距的存在,以及看起来没有任何空TextBlock
功能的元素通常表示布局过于复杂。布局中不必要的复杂性使得实现您想要的效果非常困难。我正在使用另一种简单的方法,它允许我不更改文档布局
主要思想是在控件开始更改之前不要设置控件的宽度。对于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;
}
私有无效文本框\u SizeChanged(对象发送方,SizeChangedEventArgs e)
{
FrameworkElement框=(FrameworkElement)发送方;
如果(e.PreviousSize.Width==0 | | box.Width
或者,您可以将文本块的宽度
绑定到父级的实际宽度
,例如:
<TextBlock Width="{Binding ElementName=*ParentElement*, Path=ActualWidth}"
Height="Auto" />
这将迫使它也自动调整其高度。您可以使用扩展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);
}
}
}
使用MaxWidth
和TextWrapping=“WrapWithOverflow”
不知道如何表达我的感谢,但这确实是一个非常好的回答。你让我理解了这个问题,并提供了一个非常好的解决方案,用非常简单的话来说。是的,我应该在发帖前清理一下,因为这样会分散我对问题的注意力。我们将为下一次学习。再次感谢,回答得好,伊恩。没有时间确认,但想建议在文本块上设置MaxWidth+1如果功能被封装在行为中,则更符合WPF的含义。你不应该对控件进行子类化。这是事实,但在我的例子中,它在整个系统中非常普遍,我只是想确保在所有实例中都使用相同的行为,这是一种更好的维护方法。总的来说,我认为最好记住,在软件中,就像在生活中一样,没有一条规则适合所有人。
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);
}
}
}