Prevent a TextBox from horizontal expanding in WPF

2019-02-16 21:50发布

问题:

I have the following style defined in my 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>

And throughout the solution we're using it on every text box that needs a brief text.

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

Everything works great, but then the client complains about some fields were corped on older monitors with lower resolutions, so I placed a ScrollViewer on one of the higher visual tree nodes to prevent the corping.

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

Strangely, the TextBoxes with the above style start expanding to the right instead of wrapping the text.

Is there a way to prevent this without removing the ScrollViewer?

回答1:

You must define a MaxWidth for the TextBox, otherwise there's no limit because the ScrollViewer.



回答2:

If you don't want to hard code the width then you can go for element binding the width of the parent item... here is a sample

Here i am binding textbox maxwidth with scrolviewer actual width.. and also you have to make sure that the columndefinition width should be set to "*" not to "Auto". if you set it to Auto it will neglect the ScrollViewer width and keep on expanding the width of ScrollViewer and textbox.. i think you fall in this case....

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


回答3:

The solution provided from @bathineni helped me solve my problem. Here is what worked for me:

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


回答4:

Works for me. If you want scrollbars to appear in the textbox, you may add

HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto"

to the TextBox



回答5:

I am not sure why but I could not get the ScrollViewer Solution to work. I needed to have a textbox with a fixed initial width to implement a numeric up/down control - in this control the textbox was shrinking and growing in dependence of the input which looks very anoying if the UI changes as you type.

So, I found the below solution using 2 textboxes to work for me. The first textbox is the textbox displayed for th user to type his input and the 2nd textbox is initialized through a dependency property (DisplayLength) and the converter shown further below.

Binding the MaxWidth property of the 1st TextBox to the Width property of the 2nd TextBox fixes the size such that users can type what they want but the displayed width of the textbox will not change even if there is more UI space available ...

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