12
votes

Comment configurer un contrôle de zone de texte pour vous redimensionner automatiquement lorsque le texte ne correspond plus sur une ligne?

Comment puis-je configurer un contrôle de texte code> textbox de code> pour vous redimensionner automatiquement lorsque le texte ne correspond plus sur une ligne?

Par exemple, dans le XAML suivant: P>

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


0 commentaires

5 Réponses :


46
votes

Bien que la suggestion d'Andre Luus soit essentiellement correcte, elle ne fonctionnera pas ici, car votre mise en page vaincre l'enveloppement de texte. Je vais expliquer pourquoi.

Fondamentalement, le problème est-ce: l'emballage de texte ne fait que quelque chose lorsque la largeur d'un élément est contrainte, mais votre textbox code> a une largeur non contrainte car c'est un descendant d'un "code" horizontal > StackPanel code>. (Eh bien, deux panneaux de pile horizontaux. Peut-être plus, en fonction du contexte à partir duquel vous avez pris votre exemple.) Depuis que la largeur est non contrainte, la zone code> code> n'a aucune idée quand il est censé commencer à emballer, et Donc, il n'enveloppera jamais, même si vous activez Emballage. Vous devez faire deux choses: contraignez sa largeur et activez l'emballage. P>

Voici une explication plus détaillée. P>

Votre exemple contient beaucoup de détail sans pertinence au problème. Voici une version que j'ai coupée quelque peu pour faciliter l'expliquer ce qui ne va pas: p> xxx pré>

donc j'ai supprimé votre contenant dockpanel code> et le Deux bordeaux code> à l'intérieur de cela, car ils ne font ni partie du problème ni pertinent pour la solution. Je commence donc à la paire de StackPanel CODE> Éléments de votre exemple. Et j'ai également supprimé la plupart des attributs parce que la plupart d'entre eux ne sont pas pertinents pour la mise en page. P>

Ceci semble un peu bizarre - avoir deux panneaux de pile horizontale imbriqués comme celui-ci a été redondant, mais il fait en fait Créez un sens dans votre original si vous devez rendre la personne imbriquée visible ou invisible au moment de l'exécution. Mais il est plus facile de voir le problème. P>

(la balise code> textblock code> est également bizarre, mais c'est exactement tel qu'il apparaît dans votre original. Cela ne semble pas être faire quelque chose d'utile.) p>

et voici le problème: votre Textbox > est à l'intérieur de certains StackPanel code> Eléments, ce qui signifie que sa largeur est non contrainte - vous avez dit par inadvertance La zone de texte qu'il est libre de croître à n'importe quelle largeur, quelle que soit la quantité d'espace disponible. P>

A StackPanel code> effectuera toujours la mise en page non contrainte dans la direction de l'empilement . Donc, quand il s'agit de poser que textbox code>, il passera dans une taille horizontale de double.Posititritisinfinité code> à la zone code>. Donc, la zone code> code> pensera toujours qu'il a plus d'espace qu'il n'en a besoin. De plus, lorsqu'un enfant d'un StackPanel code> demande plus d'espace que c'est réellement disponible, le stackpanel CODE> réside, et prétend lui donner cette grande place, mais puis la culture.

(c'est le prix que vous payez pour la simplicité extrême de StackPanel Code> - C'est simple au point d'être dirigé par des os, car il construira volontiers des mises en page qui ne conviennent pas réellement . Vous ne devez utiliser que stackpanel code> si vous avez vraiment un espace illimité, car vous êtes à l'intérieur d'un ScrollViewer code> ou vous êtes certain que vous avez suffisamment peu d'articles que vous avez Ne pas manquer d'espace ou si vous ne vous souciez pas des articles qui descendent de la fin du panneau lorsqu'ils deviennent trop volumineux et que vous ne voulez pas que le système de mise en page tente de faire quelque chose de plus intelligent que de simplement recadrer le contenu. .) p>

Donc, ce qui allume l'emballage de texte ne vous aidera pas ici, car le stackpanel code> prétendez toujours qu'il y a plus d'espace pour le texte. P>

Vous n eed une autre structure de mise en page. Les panneaux de pile sont la mauvaise chose à utiliser car ils n'imposeront pas la contrainte de mise en page dont vous avez besoin d'emballage de texte pour lancer. P>

Voici un exemple simple qui fait grossièrement ce que vous voulez: p>

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


2 commentaires

Je ne sais pas comment exprimer mes remerciements, mais c'est une réponse vraiment excellente. Vous avez fait comprendre le problème et proposé une très bonne solution en les mettant en termes très simples. Oui, j'aurais dû nettoyer les choses un peu avant de poster comme il distrait du problème. Apprendra pour la prochaine fois. Merci encore.


Belle réponse ian. N'avait pas le temps de confirmer mais voulait suggérer de définir Maxwidth sur le texte TextBlock. +1



0
votes

J'utilise une autre approche simple qui me permet de ne pas changer la mise en page du document.

L'idée principale n'est pas de définir la largeur de contrôle code> avant de commencer à changer. Pour Textbox code> ES, je gère le SIZECHANGED CODE> EVENT: P>

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


0 commentaires

8
votes

Alternativement, vous pouvez contraindre votre textblock code> de la largeur code> en le liant au parent réelwidth code>, par exemple:

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


0 commentaires

0
votes

Vous pouvez utiliser cette classe qui étend la textblock. Il se rétrécit automatiquement et prend en compte 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);
            }
        }
    }


2 commentaires

Ce serait davantage au sens de WPF si la fonctionnalité est encapsulée dans un comportement. Vous ne devriez pas sous-classer des contrôles.


C'est vrai, mais dans mon cas, il était si couramment utilisé dans notre système que je voulais juste m'assurer que le même comportement est utilisé dans tous les cas et que c'était un meilleur moyen de le maintenir. En général, je pense que c'est bon de garder à l'esprit que, dans des logiciels, comme dans la vie elle-même, il n'existe aucune règle pour les adapter à tous.



2
votes

Utiliser maxwidth et TextWrapping = "wrapwithoverflow" .


0 commentaires