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

时间:2014-01-21 10:33:21

标签: .net wpf xaml layout

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

考虑这个简单的窗口:

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

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

  1. 此设置将使第二个文本框动态调整大小,以便使用剩余的窗口空间。
  2. 如果窗口对于ScrollViewer内容所需的最小尺寸而言太小,则ScrollViewer会显示滚动条。
  3. 但是,当您在第二个文本框中输入太多文字时,ScrollViewer会显示滚动条,而不会显示TextBox。我想停止文本框的高度增加超出父Grid最初给出的空间。在这种情况下,我无法使用MaxHeight,因为没有合适的ActualHeight可以绑定(据我所见)。

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

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

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

6 个答案:

答案 0 :(得分:6)

可滚动扩展控制问题。

Scrollable-expandable-controls:控件可以随着内容的增长而拉伸,并在其大小受限时显示滚动条。

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

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

为了实现所需的行为,我们需要在它们之间使用一个面板来允许它的子节点(可滚动扩展控件)增长,要求它们提供所需的最小大小,然后为它们提供父节点提供的最大大小而不显示滚动条,目前没有这样的面板。

这是我开发的用于提供此功能的一个:

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

然后在你的xaml里面封装你的可滚动扩展控件。

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

答案 1 :(得分:2)

试试这个:

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

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

答案 2 :(得分:1)

试试这个:

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

你完成了:)

答案 3 :(得分:1)

沿着该行的某处,您必须设置启用滚动的文本框的最大高度。通常*会这样做,但因为它包含在滚动查看器中,所包含的网格没有有效的最大值。 您可以像这样使用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();
    }
}

然后您的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>

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

希望这有帮助

答案 4 :(得分:0)

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

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

<小时/> 更新

如果您只为 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>        

答案 5 :(得分:0)

我想出了类似的东西,但是我还在测试,只需要知道这是不是你想象的那样?

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

背后的代码

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

<强>更新

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

希望这有帮助。

更新2

我已经更新了我的XAML和Code,这对我有用。 (我想)。