12

TextBoxテキストが1行に収まらなくなったときに、コントロールのサイズを自動的に垂直方向に変更するようにコントロールを構成するにはどうすればよいですか?

たとえば、次のXAMLでは次のようになります。

<DockPanel LastChildFill="True" Margin="0,0,0,0">
  <Border Name="dataGridHeader" 
    DataContext="{Binding Descriptor.Filter}"
    DockPanel.Dock="Top"                         
    BorderThickness="1"
    Style="{StaticResource ChamelionBorder}">
  <Border
    Padding="5"
    BorderThickness="1,1,0,0"                            
    BorderBrush="{DynamicResource {ComponentResourceKey TypeInTargetAssembly=dc:NavigationPane, 
    ResourceId={x:Static dc:NavigationPaneColors.NavPaneTitleBorder}}}">
    <StackPanel Orientation="Horizontal">
      <TextBlock                                
        Name="DataGridTitle"                                                                                                
        FontSize="14"
        FontWeight="Bold"                                    
        Foreground="{DynamicResource {ComponentResourceKey 
        TypeInTargetAssembly=dc:NavigationPane, 
        ResourceId={x:Static dc:NavigationPaneColors.NavPaneTitleForeground}}}"/>
      <StackPanel Margin="5,0"  Orientation="Horizontal" 
              Visibility="{Binding IsFilterEnabled, FallbackValue=Collapsed, Mode=OneWay, Converter={StaticResource BooleanToVisibility}}"
              IsEnabled="{Binding IsFilterEnabled, FallbackValue=false}"  >                                    
          <TextBlock  />                                                                
          <TextBox    
            Name="VerticallyExpandMe"
            Padding="0, 0, 0, 0"  
            Margin="10,2,10,-1"                                                                                                                                                                                                                     
            AcceptsReturn="True"
            VerticalAlignment="Center"                                    
            Text="{Binding QueryString}"
            Foreground="{DynamicResource {ComponentResourceKey 
            TypeInTargetAssembly=dc:NavigationPane, 
            ResourceId={x:Static dc:NavigationPaneColors.NavPaneTitleForeground}}}">
          </TextBox>
        </StackPanel>                               
    </StackPanel>
  </Border>              
  </Border>
</DockPanel>

「VerticallyExpandMe」という名前のTextBoxコントロールは、バインドされたテキストが1行に収まらない場合、自動的に垂直方向に展開する必要があります。AcceptsReturntrueに設定するとTextBox、Enterキーを押すと垂直方向に展開しますが、これを自動的に実行したいと思います。

4

5 に答える 5

46

Andre Luusの提案は基本的に正しいですが、レイアウトによってテキストの折り返しが無効になるため、実際にはここでは機能しません。その理由を説明します。

基本的に、問題はこれです。テキストの折り返しは、要素の幅が制限されている場合にのみ何も行いませんがTextBox、水平の子孫であるため、制限されていない幅がありますStackPanel。(2つの水平スタックパネル。例を示したコンテキストによっては、さらに多くなる可能性があります。)幅に制約がないため、TextBoxいつ折り返しを開始するかわからないため、たとえ折り返しが行われることはありません。ラッピングを有効にします。2つのことを行う必要があります。幅を制限することと、折り返しを有効にすることです。

これがより詳細な説明です。

あなたの例には、問題に関係のない多くの詳細が含まれています。何が悪いのかを説明しやすくするために、私がいくらかトリミングしたバージョンを次に示します。

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

それで、私はあなたの包含要素DockPanelとその中の2つのネストされた要素を削除しましBorderた。なぜなら、それらは問題の一部でも解決策にも関連していないからです。だから私はStackPanelあなたの例のネストされた要素のペアから始めています。また、ほとんどの属性はレイアウトに関連していないため、ほとんどの属性も削除しました。

これは少し奇妙に見えます-このようにネストされた水平スタックパネルが2つあると冗長に見えますが、実行時にネストされたものを表示または非表示にする必要がある場合は、実際には元のパネルで意味があります。ただし、問題がわかりやすくなります。

(空のTextBlockタグも奇妙ですが、それは元のタグとまったく同じです。それは何の役にも立たないようです。)

そして、ここに問題があります。あなたTextBoxはいくつかの水平要素の内側StackPanelにあり、その幅は制約されていません-実際に利用可能なスペースの量に関係なく、任意の幅に自由に拡張できることをテキストボックスに誤って伝えました。

AStackPanelは、常にスタッキングの方向に制約のないレイアウトを実行します。したがって、それをレイアウトする場合は、の水平サイズでにTextBox渡されます。したがって、は常に必要以上のスペースがあると考えます。さらに、子供が実際に利用できるよりも多くのスペースを要求すると、嘘をつき、そのスペースを与えるふりをしますが、それをトリミングします。double.PositiveInfinityTextBoxTextBoxStackPanelStackPanel

(これは、非常に単純なために支払う代償ですStackPanel。実際には収まらないレイアウトをうまく構築できるため、骨の折れるほど単純です。StackPanelどちらかが本当に無制限のスペースを持っている場合にのみ使用してください。の中にいるScrollViewer、またはスペースが不足しないようにアイテムが十分に少ないと確信している、またはアイテムが大きくなりすぎてパネルの端から流れ出るのを気にしない場合レイアウトシステムに、単にコンテンツをトリミングするよりも賢いことをさせたくない場合。)

したがって、テキストの折り返しをオンにしても、ここでは役に立ちませんStackPanel。これは、テキストに十分なスペースがあると常にふりをするためです。

別のレイアウト構造が必要です。スタックパネルは、テキストの折り返しを開始するために必要なレイアウトの制約を課さないため、使用するのは間違っています。

これは、おおよそあなたが望むことをする簡単な例です:

<Grid VerticalAlignment="Top">
    <DockPanel>
        <TextBlock
            x:Name="DataGridTitle"
            VerticalAlignment="Top"
            DockPanel.Dock="Left"
            />
        <TextBox
            Name="VerticallyExpandMe"
            AcceptsReturn="True"
            TextWrapping="Wrap"
            Text="{Binding QueryString}"
            >
        </TextBox>
    </DockPanel>
</Grid>

まったく新しいWPFアプリケーションを作成し、それをメインウィンドウのコンテンツとして貼り付けると、必要な処理が行われることがわかりますTextBox。最初は1行の高さで始まり、使用可能な幅を埋め、テキストを入力すると、次のようになります。テキストを追加すると、一度に1行ずつ大きくなります。

もちろん、レイアウトの動作は常にコンテキストに敏感であるため、既存のアプリケーションの真ん中にそれを投げ込むだけでは不十分な場合があります。これは、固定サイズのスペース(ウィンドウの本体など)に貼り付けると機能しますが、幅に制約がないコンテキストに貼り付けると正しく機能しません。(たとえば、の内側ScrollViewer、または水平の内側StackPanel。)

したがって、これがうまくいかない場合は、レイアウトの他の場所で他の問題が発生している可能性があります。おそらく、他の場所でさらに多くのStackPanel要素が発生している可能性があります。あなたの例の見た目から、あなたがあなたのレイアウトに本当に必要なものを考えてそれを単純化するのに時間を費やす価値があるでしょう-負のマージンの存在、そしてそのような空のようなことをしないように見える要素TextBlockは通常過度に複雑なレイアウト。また、レイアウトが不必要に複雑になると、探している効果を実現するのが非常に難しくなります。

于 2011-03-11T16:46:51.633 に答える
8

または、次のように、親にバインドして自分TextBlockのを制約することもできます。WidthActualWidth

<TextBlock Width="{Binding ElementName=*ParentElement*, Path=ActualWidth}" 
           Height="Auto" />

これにより、高さも自動的にサイズ変更されます。

于 2012-05-02T13:19:50.853 に答える
2

とを使用MaxWidthTextWrapping="WrapWithOverflow"ます。

于 2014-02-06T00:16:46.807 に答える
0

ドキュメントのレイアウトを変更しないようにする別の簡単なアプローチを使用しています。

主なアイデアは、Width変更を開始する前にコントロールを設定しないことです。TextBoxesの場合、私はイベントを処理します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;
}
于 2011-12-18T10:35:33.573 に答える
0

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);
            }
        }
    }
于 2013-10-15T09:05:46.193 に答える