防止WPF中的TextBox在水平方向上扩展

13

我在App.xaml中定义了以下样式

<Style x:Key="textBoxMultiline" TargetType="{x:Type TextBox}" >
    <Setter Property="VerticalScrollBarVisibility" Value="Auto" />
    <Setter Property="HorizontalScrollBarVisibility" Value="Hidden" />
    <Setter Property="MinHeight" Value="50" />
    <Setter Property="TextWrapping" Value="Wrap" />
</Style>

在整个解决方案中,我们将它应用于需要简短文本的每个文本框。

<TextBox x:Name="textBoxDescription" Grid.Row="2" Grid.Column="1" Style="{DynamicResource textBoxMultiline}" />

一切都很顺利,但客户抱怨在分辨率较低的旧显示器上一些字段被截断了,因此我在较高的可视树节点之一上放置了一个ScrollViewer以防止截断。

<ScrollViewer Height="Auto" VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Auto">
   ...
</ScrollViewer>

奇怪的是,应用了上述样式的 TextBox 开始向右扩展而不是包裹文本。

有没有办法在不移除 ScrollViewer 的情况下防止这种情况发生?

8个回答

11

如果你不想硬编码宽度,那么你可以选择使用元素绑定父项的宽度。

在这里,我将TextBox的最大宽度与ScrollViewer的实际宽度进行绑定。你还需要确保ColumnDefinition的宽度应该设置为“ * ”而不是“ Auto ”。如果你将它设置为Auto,它将忽略ScrollViewer的宽度并继续扩展ScrollViewer和TextBox的宽度。我认为你就是处于这种情况下...

<Grid>
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="*"></ColumnDefinition>
        <ColumnDefinition Width="*"></ColumnDefinition>
    </Grid.ColumnDefinitions>
    <ScrollViewer HorizontalScrollBarVisibility="Auto" Name="scv">
        <TextBox Height="30" TextWrapping="Wrap" MaxWidth="{Binding ElementName=scv, Path=ActualWidth}"></TextBox>
    </ScrollViewer>
</Grid>

1
很遗憾的是,在ScrollViewer和TextBox之间有几个VisualTreeItems,而且它们甚至不在同一个UserControl上。尽管如此,仍然存在边距/填充偏移。据我所知,真正的挑战在于将TextWrapping优先于祖先UserControl的水平调整大小。 - dcarneiro

9

对于TextBox,您必须定义MaxWidth,否则由于ScrollViewer的原因,没有限制。


我正在使用文本框具有不同宽度的那种样式。我在寻找更通用的解决方案,比如重写文本框模板以包含固定宽度的元素,然后将文本框实际宽度绑定到该新元素的宽度。这似乎有点过度工程,但我正在寻找的只是改变文本框样式的东西。 - dcarneiro
你所做的有些矛盾。你将文本框的宽度限制在一定范围内,然后将其放置在一个水平空间不受限制(“Auto”)的滚动视图器中。你可以解决这个问题,但没有通用的方法。你应该从滚动视图器的实际宽度和文本框的(最大)宽度创建一个绑定。问题是你必须添加一个转换器,以考虑偏移量和其他东西(对我来说非常烦人)。 - Mario Vernari
水平滚动条是一种hack,因为在低分辨率屏幕上其他控件被裁剪了。我试图将文本框的宽度绑定到一个相同宽度的元素上(同一网格单元中的另一个文本框,具有相同的样式但隐藏),并且它起作用了。但后来我发现信息要显示的TextBlock也会水平扩展而不是换行文本。这告诉我水平滚动条优先于TextWrapping属性。所以我想我必须重新设计UI以支持低屏幕分辨率。 - dcarneiro

6

@bathineni提供的解决方案帮助我解决了我的问题。以下是对我有用的解决方案:

<Grid >
    <Grid.ColumnDefinitions>
    <ColumnDefinition Width="50"/>
    <ColumnDefinition  Width="*"/>
</Grid.ColumnDefinitions>
    <Button Grid.Column="0" Width="30" Height="23" Margin="10,5" Content="..."/>
    <ScrollViewer  Grid.Column="1" HorizontalScrollBarVisibility="Disabled" verticalScrollBarVisibility="Disabled" Name="scv">
         <TextBox Height="25" Text="Insert here long text" MaxWidth="{Binding ElementName=scv, Path=ActualWidth}" HorizontalAlignment="Stretch" />
    </ScrollViewer>
</Grid>

1
我尝试了上述的例子,但它们都没有起作用,所以我自己解决了这个问题。有两种解决方法:
第一种解决方案是使用数据绑定在XAML中实现的。我建议您不要将控件绑定到自身。XAML解决方案是通过将控件与所需的ActualWidth和ActualHeight属性绑定到文本框的MaxHeight和MaxWidth属性来实现的。
<TextBlock x:Name="PasswordText" Margin="0,0,0,20" FontFamily="Bahnschrift SemiBold Condensed" Text="PASSWORD" FontSize="20"> 

 
<TextBox x:Name="PasswordTextBox" MaxWidth="{Binding ElementName=PasswordText, Path=ActualWidth}" MaxHeight="{Binding ElementName=PasswordText, Path=ActualHeight}">

下一个解决方案是通过在XAML中生成一个Loaded事件,在C#代码中创建它,然后在Loaded事件中将文本框的MaxWidth和MaxHeight属性设置为文本框的ActualWidth和ActualHeight属性来实现。
//  It doesn't have a problem like in XAML if you pass the textbox its own   
//  ActualWidth and ActualHeight to the MaxWidth and MaxHeight proprieties. 


        private void Log_In_Page_Loaded(object sender, RoutedEventArgs e)
        {
            UsernameTextBox.MaxHeight = UsernameTextBox.ActualHeight;
            UsernameTextBox.MaxWidth = UsernameTextBox.ActualWidth;
        }

选择适合您设计的那一个,但我认为,在我的意见中,这是最有效、简单、稳定和有效解决这个问题的方法。


0
当我需要我的TextBox随着其自动调整大小的Grid列一起拉伸而被调整大小时,我遇到了这个问题,这意味着MaxWidth不起作用,但仍然需要防止TextBox随着其内容一起拉伸。
我最终做的是将此事件处理程序链接到SizeChanged
private void TextBox_SizeChanged(object sender, SizeChangedEventArgs e) {
    TextBox textBox = (TextBox)sender;
    if (textBox.CanUndo && e.NewSize.Width > e.PreviousSize.Width) {
        textBox.Width = e.PreviousSize.Width;
    }
}

将文本写入文本框是用户可以撤消的操作,而其他可能导致调整大小的操作(元素的初始绘制、父容器的拉伸等)无法从文本框内部撤消。因此,通过检查CanUndo,我们可以确定SizeChanged是由编写文本还是其他原因触发的。

e.NewSize.Width > e.PreviousSize.Width检查是必要的,因为如果没有它,SizeChanged事件将无限地从自身调用,因为要恢复拉伸,我们需要将大小更改回原始大小,这本身会触发事件。

这有点巧妙,但我还没有遇到任何问题。


0

我不确定为什么无法使用ScrollViewer解决方案。我需要一个具有固定初始宽度的TextBox来实现数字上下控件 - 在此控件中,TextBox独立于输入而收缩和扩展,如果UI随着您的键入而更改,则看起来非常烦人。

因此,我发现以下使用2个文本框的解决方案适合我。第一个文本框是显示给用户输入的文本框,第二个文本框通过依赖属性(DisplayLength)和下面显示的转换器进行初始化。

将第一个TextBox的MaxWidth属性绑定到第二个TextBox的Width属性可以修复大小,使用户可以键入所需内容,但即使有更多的UI空间可用,文本框的显示宽度也不会改变。

<TextBox x:Name="PART_TextBox"
    Text="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Value}"
    Margin="0,0,1,0"
    TextAlignment="Right"
    AcceptsReturn="False"
    SpellCheck.IsEnabled="False"
    HorizontalContentAlignment="Stretch"
    VerticalContentAlignment="Center"
    HorizontalAlignment="Stretch"
    VerticalAlignment="Stretch"
    MaxWidth="{Binding ElementName=TBMeasure, Path=ActualWidth}"
    />

<!-- Hidden measuring textbox ensures reservation of enough UI space
     according to DisplayLength dependency property
-->
<TextBox x:Name="TBMeasure"
    Text="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=DisplayLength, Converter={StaticResource ByteToPlaceHolderStringConverter}}"
    Margin="0,0,1,0"    
    TextAlignment="Right"
    AcceptsReturn="False"
    SpellCheck.IsEnabled="False"
    HorizontalContentAlignment="Right"
    VerticalContentAlignment="Center"
    HorizontalAlignment="Stretch"
    VerticalAlignment="Stretch"
    Visibility="Hidden"/>



// Converter
[ValueConversion(typeof(byte), typeof(string))]
public sealed class ByteToPlaceHolderStringConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        if ((value is byte) == false)
            return Binding.DoNothing;

        byte byteVal = (byte)value;

        string retString = string.Empty;
        for (int i = 0; i < byteVal; i++)
            retString = retString + "X";

        return retString;
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        return Binding.DoNothing;
    }
}

0

对我来说可以。如果你想让文本框出现滚动条,你可以添加

HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto"

到文本框中


0

您必须设置容器控件最大宽度

<Grid x:Name="RootGrid" Margin="6,6,8,8" Width="500" MaxWidth="500">
<ScrollViewer ScrollViewer.HorizontalScrollBarVisibility="Disabled" ScrollViewer.VerticalScrollBarVisibility="Auto">
  <Grid>
    <Grid.RowDefinitions>
      <RowDefinition Height="Auto"/>
    </Grid.RowDefinitions>
    <GroupBox Name="contentGroup" Header="Content" Grid.Row="0">
      <TextBox Name="content"/>
    </GroupBox>
  </Grid>
</ScrollViewer>


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接