WPF:如何使文本框动态调整大小但防止自动调整大小?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/21255551/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-13 10:34:09  来源:igfitidea点击:

WPF: How to make a textbox dynamically sized yet prevent autosizing?

.netwpfxamllayout

提问by floele

I know there are a lot of questions in regard to the auto sizing of text boxes in WPF, but I couldn't find a solution for the following problem.

我知道有很多关于 WPF 中文本框自动调整大小的问题,但我找不到以下问题的解决方案。

Consider this simple window:

考虑这个简单的窗口:

<Window x:Class="TestVisualBrush.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="MainWindow" Height="470" Width="608">
<ScrollViewer>
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto" />
            <RowDefinition Height="*" />
        </Grid.RowDefinitions>

        <TextBox>Test</TextBox>
        <TextBox MinHeight="100" Grid.Row="1" AcceptsReturn="True" >Test</TextBox>
    </Grid>
</ScrollViewer>
</Window>

This implements these two constraints I need:

这实现了我需要的这两个约束:

  1. This setup will make the second text box sized dynamically so that it uses the remaining window space.
  2. If the window becomes too small for the required minimum size of the ScrollViewer's contents, the ScrollViewershows a scrollbar.
  1. 此设置将使第二个文本框动态调整大小,以便它使用剩余的窗口空间。
  2. 如果窗口对于 的内容所需的最小尺寸来说变得太小ScrollViewer,则ScrollViewer显示滚动条。

However, when you type too much text in the second text box, the ScrollViewershows a scrollbar, instead of the TextBox. I'd like to stop the text box from increasing its height beyond the space given by the parent Gridoriginally. I can't use MaxHeightin this case because there is no suitable ActualHeightto bind to (as far as I can see).

但是,当您在第二个文本框中键入过多文本时,将ScrollViewer显示滚动条,而不是TextBox. 我想阻止文本框将其高度增加到超出父级Grid最初给定的空间。我不能MaxHeight在这种情况下使用,因为没有合适ActualHeight的绑定(据我所知)。

Any suggestions on how to solve this (preferably without code-behind)?

关于如何解决这个问题的任何建议(最好没有代码隐藏)?

Note that the root ScrollViewer should still scroll if its contents are too large for the window.

请注意,如果其内容对于窗口来说太大,则根 ScrollViewer 仍应滚动。

In HTML what I want would translate to this:

在 HTML 中,我想要的将转换为:

<table height="100%">
 <tr>
    <td><input type="text"></td>
 </tr>
 <tr height="100%"> 
    <td>
          <!-- Uses as much space as it gets, but scrolls if text inside
               gets too large. Makes outer window scroll if too small
               for min-height and other controls in table. -->
      <textarea style="height:100%;min-height:100px"></textarea>
    </td>
  </tr>
</table>

采纳答案by Ashi

Scrollable-expandable-controls problem.

可滚动扩展控件问题。

Scrollable-expandable-controls : are controls that can stretch as its content grows and will display scrollbars when their size is restricted.

Scrollable-expandable-controls :这些控件可以随着其内容的增长而拉伸,并且当它们的大小受到限制时将显示滚动条。

Problem appears when they are located inside another scrollable control. Child scrollable-expandable-controls will keep expanding and will count on the outer scrollable control's scrollbars.

当它们位于另一个可滚动控件内时会出现问题。子滚动可扩展控件将继续扩展并依赖于外部可滚动控件的滚动条。

if you give it a maximum width or height problem will be resolved but you will need to know the size ahead and you don't have this privilege if you want a dynamic app that works well with all different screen sizes.

如果你给它一个最大宽度或高度,问题将得到解决,但你需要知道前面的尺寸,如果你想要一个适用于所有不同屏幕尺寸的动态应用程序,你就没有这个特权。

in order to achieve required behavior , we need a panel in between to allow its children (scrollable-expandable-control) to grow asking them to give the minimum required size and then give them the maxiumum size the parent provides without displaying scrollbars , currently there is no panel like this.

为了实现所需的行为,我们需要一个介于两者之间的面板,以允许其子项(可滚动扩展控件)增长,要求它们提供所需的最小尺寸,然后为它们提供父级提供的最大尺寸,而不显示滚动条,目前在那里没有这样的面板。

Here is a one that I developed to provide this functionality:

这是我为提供此功能而开发的:

    class LimitChild : System.Windows.Controls.Panel
    {
        public LimitChild()
        {
        }

        protected override Size MeasureOverride(System.Windows.Size availableSize)
        {
            System.Diagnostics.Debug.Assert(InternalChildren.Count == 1);
            System.Windows.UIElement child = InternalChildren[0];

            Size panelDesiredSize = new Size();
            // panelDesiredSize.Width = availableSize.Width;
            panelDesiredSize.Width = (double)child.GetValue(FrameworkElement.MinWidthProperty);
            panelDesiredSize.Height = (double)child.GetValue(FrameworkElement.MinHeightProperty);

            child.Measure(panelDesiredSize);

            // IMPORTANT: do not allow PositiveInfinity to be returned, that will raise an exception in the caller! 
            // PositiveInfinity might be an availableSize input; this means that the parent does not care about sizing 
            return panelDesiredSize;
        }

        protected override System.Windows.Size ArrangeOverride(System.Windows.Size finalSize)
        {
            System.Windows.UIElement child = InternalChildren[0];

            child.Arrange(new Rect(0, 0, finalSize.Width, finalSize.Height));
            if (finalSize.Width > child.RenderSize.Width)
                finalSize.Width = child.RenderSize.Width;
            if (finalSize.Height > child.RenderSize.Height)
                finalSize.Height = child.RenderSize.Height;

            return finalSize; // Returns the final Arranged size
        }
    }

and then inside your xaml incapsulate your scrollable-expandable-control in it.

然后在您的 xaml 中将您的可滚动扩展控件封装在其中。

        <l:LimitChild
            Grid.Row="1">
            <TextBox
                VerticalScrollBarVisibility="Auto"
                HorizontalScrollBarVisibility="Auto"
                MinHeight="200"
                AcceptsReturn="True">Test</TextBox>
        </l:LimitChild>

回答by Torben Schramme

Try this:

尝试这个:

<Window x:Class="WpfApplication1.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="Window1" Height="300" Width="300">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto" />
            <RowDefinition Height="*" />
        </Grid.RowDefinitions>
        <TextBox />
        <TextBox AcceptsReturn="True" Grid.Row="1" VerticalScrollBarVisibility="Auto" />
    </Grid>
</Window>

This should exactly meet your requirements.

这应该完全符合您的要求。

回答by Naresh Ravlani

Try this :

尝试这个 :

<Grid>

        <Grid.RowDefinitions>
            <RowDefinition Height="Auto" />
            <RowDefinition Height="*" />
        </Grid.RowDefinitions>

        <TextBox Height="50"/>

        <TextBox Grid.Row="1" AcceptsReturn="True" ScrollViewer.HorizontalScrollBarVisibility="Disabled" ScrollViewer.VerticalScrollBarVisibility="Auto" />

    </Grid>

And you are done :)

你完成了:)

回答by CaptainHook

Somewhere along the line you have to set the max height of the scroll enabled text box. Normally * would do that but because it is wrapped in a scroll viewer the containing grid has no effective maximum. You could use a MultiValueConverter like this

沿着这条线的某个地方,您必须设置启用滚动的文本框的最大高度。通常 * 会这样做,但是因为它包含在滚动查看器中,所以包含的网格没有有效的最大值。你可以像这样使用 MultiValueConverter

    public class MaxHeightConverter : IMultiValueConverter
{
    public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        double result = (double)values[0];
        for (int n = 1; n < values.Length; ++n)
        {
            result = result - (double)values[n];
        }
        return result;
    }

    public object[] ConvertBack(object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}

Then your XAML will look like this

然后您的 XAML 将如下所示

<Window x:Class="wpf_Hacks.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:wpf_Hacks"
    Title="MainWindow" Height="350" Width="525">
<Window.Resources>
    <local:MaxHeightConverter x:Key="maxHeightConverter"></local:MaxHeightConverter>
</Window.Resources>
<ScrollViewer>
    <Grid x:Name="rootGrid">
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto" />
            <RowDefinition Height="*" />
        </Grid.RowDefinitions>

        <TextBox x:Name="headerTextBox" Height="50" Text="Textbox 1"/>

        <TextBox MinHeight="100" Grid.Row="1" AcceptsReturn="True" ScrollViewer.HorizontalScrollBarVisibility="Disabled" ScrollViewer.VerticalScrollBarVisibility="Auto" Text="Textbox 2">
            <TextBox.MaxHeight>
                <MultiBinding Converter="{StaticResource maxHeightConverter}">
                    <Binding ElementName="rootGrid" Path="ActualHeight"></Binding>
                    <Binding ElementName="headerTextBox" Path="ActualHeight"></Binding>
                </MultiBinding>
            </TextBox.MaxHeight>
        </TextBox>

    </Grid>
</ScrollViewer>

Basically the multi value converter takes the first bound value as the total height available and then reduces this by the heights of the other bound elements leaving the remaining space for the final textbox

基本上,多值转换器将第一个绑定值作为可用的总高度,然后通过其他绑定元素的高度将其减少,为最终文本框留下剩余空间

Hope this helps

希望这可以帮助

回答by Max Mazur

I came up with something like this, however i'm still testing, just need to know if this is what you imagined?

我想出了这样的东西,但是我仍在测试中,只需要知道这是否是您的想象?

<Window x:Class="testWpf.WindowTextBoxInnerScroll"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="200" Width="608" Name="MyWindow" Loaded="MyWindow_Loaded">

<ScrollViewer VerticalScrollBarVisibility="Auto" >
<Grid name="MyGrid">
    <Grid.RowDefinitions>
        <RowDefinition Height="Auto" />
        <RowDefinition Height="*" />
    </Grid.RowDefinitions>

        <TextBox>Test</TextBox>
        <TextBox Name="TB2" Grid.Row="1" MinHeight="100" TextWrapping="Wrap" 
                 VerticalScrollBarVisibility="Auto" AcceptsReturn="True" >Test</TextBox>
</Grid>
</ScrollViewer>

Code behind

背后的代码

private void MyWindow_Loaded(object sender, RoutedEventArgs e)
{
  if (this.Height > MyGrid.RowDefinitions[0].ActualHeight + MyGrid.RowDefinitions[1].ActualHeight)
  {
    TB2.Height = MyGrid.RowDefinitions[1].ActualHeight;
  }
  else
  {
    TB2.Height = TB2.MinHeight;
  }
}

UPDATE

更新

If this is not what you want, then I'm uncertain of what your expectations to this question are.

如果这不是您想要的,那么我不确定您对这个问题的期望是什么。

Hope this helps.

希望这可以帮助。

UPDATE 2

更新 2

I've updated my XAML and Code behind, this is working for me. (I think).

我已经更新了我的 XAML 和代码,这对我有用。(我认为)。

回答by Anatolii Gabuza

Actually you can bind to ActualHeightby accessing ancestor using RelativeSourcebinding:

实际上,您可以ActualHeight通过使用RelativeSource绑定访问祖先来绑定:

Height="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Grid}},
                 Path=RowDefinitions[1].ActualHeight}"



Update:更新:

If you what that scroll bar should be shown only for secondTextBox- put ScrollVieweronlyfor it:

如果您应该只在第二个显示滚动条TextBox-ScrollViewer为它放置:

    <Grid>
        <Grid.RowDefinitions>
           <RowDefinition Height="Auto" />
           <RowDefinition Height="*" />
        </Grid.RowDefinitions>
        <TextBox>Test</TextBox>
        <ScrollViewer Grid.Row="1">                
            <TextBox MinHeight="100" AcceptsReturn="True" >Test</TextBox>                
        </ScrollViewer>
    </Grid>