web-dev-qa-db-fra.com

Barre de défilement verticale automatique dans WPF TextBlock?

J'ai un TextBlock dans WPF. J'écris beaucoup de lignes, dépassant de loin sa hauteur verticale. Je m'attendais à ce qu'une barre de défilement verticale apparaisse automatiquement lorsque cela se produit, mais ce n'est pas le cas. J'ai essayé de rechercher une propriété de barre de défilement dans le volet Propriétés, mais je n'ai pas pu en trouver.

Comment créer une barre de défilement verticale créée automatiquement pour ma TextBlock une fois que son contenu dépasse sa hauteur?

Précision: je préférerais le faire par le concepteur et non pas en écrivant directement au XAML.

294
Bab Yogoo

Enveloppez-le dans un visualiseur de défilement:

<ScrollViewer>
    <TextBlock />
</ScrollViewer>

NOTEcette réponse s'applique à une TextBlock (un élément de texte en lecture seule) comme demandé dans la question d'origine.

Si vous souhaitez afficher les barres de défilement dans une TextBox (un élément de texte éditable), utilisez les propriétés attachées ScrollViewer:

<TextBox ScrollViewer.HorizontalScrollBarVisibility="Disabled"
         ScrollViewer.VerticalScrollBarVisibility="Auto" />

Les valeurs valides pour ces deux propriétés sont Disabled, Auto, Hidden et Visible.

501
Drew Noakes

peut utiliser les éléments suivants maintenant:

<TextBox Name="myTextBox" 
         ScrollViewer.HorizontalScrollBarVisibility="Auto"
         ScrollViewer.VerticalScrollBarVisibility="Auto"
         ScrollViewer.CanContentScroll="True">SOME TEXT
</TextBox>
97
vince

Quelque chose de mieux serait:

<Grid Width="Your-specified-value" >
    <ScrollViewer>
         <TextBlock Width="Auto" TextWrapping="Wrap" />
    </ScrollViewer>
</Grid>

Cela garantit que le texte de votre bloc de texte ne déborde pas et ne chevauche pas les éléments situés sous le bloc de texte, comme cela peut être le cas si vous n'utilisez pas la grille. Cela m'est arrivé lorsque j'ai essayé d'autres solutions, même si le bloc de texte était déjà dans une grille avec d'autres éléments. Gardez à l'esprit que la largeur du bloc de texte doit être Auto et que vous devez spécifier l'élément désiré dans l'élément Grid. Je l'ai fait dans mon code et cela fonctionne à merveille. HTH.

19
varagrawal
<ScrollViewer Height="239" VerticalScrollBarVisibility="Auto">
    <TextBox AcceptsReturn="True" TextWrapping="Wrap" LineHeight="10" />
</ScrollViewer>

C'est une façon d'utiliser la zone de texte déroulante en XAML et de l'utiliser comme zone de texte.

5
John
<ScrollViewer MaxHeight="50"  
              Width="Auto" 
              HorizontalScrollBarVisibility="Disabled"
              VerticalScrollBarVisibility="Auto">
     <TextBlock Text="{Binding Path=}" 
                Style="{StaticResource TextStyle_Data}" 
                TextWrapping="Wrap" />
</ScrollViewer>

Je le fais d'une autre manière en mettant MaxHeight dans ScrollViewer.

Il suffit d’ajuster MaxHeight pour afficher plus ou moins de lignes de texte. Facile.

3
Tony Wu

Cette réponse décrit une solution utilisant MVVM.

Cette solution est idéale si vous souhaitez ajouter une zone de journalisation à une fenêtre, qui défile automatiquement vers le bas chaque fois qu'un nouveau message de journalisation est ajouté.

Une fois ces propriétés attachées ajoutées, elles peuvent être réutilisées n’importe où, ce qui en fait un logiciel très modulaire et réutilisable.

Ajouter ce XAML:

<TextBox IsReadOnly="True"   
         Foreground="Gainsboro"                           
         FontSize="13" 
         ScrollViewer.HorizontalScrollBarVisibility="Auto"
         ScrollViewer.VerticalScrollBarVisibility="Auto"
         ScrollViewer.CanContentScroll="True"
         attachedBehaviors:TextBoxApppendBehaviors.AppendText="{Binding LogBoxViewModel.AttachedPropertyAppend}"                                       
         attachedBehaviors:TextBoxClearBehavior.TextBoxClear="{Binding LogBoxViewModel.AttachedPropertyClear}"                                    
         TextWrapping="Wrap">

Ajouter cette propriété attachée:

public static class TextBoxApppendBehaviors
{
    #region AppendText Attached Property
    public static readonly DependencyProperty AppendTextProperty =
        DependencyProperty.RegisterAttached(
            "AppendText",
            typeof (string),
            typeof (TextBoxApppendBehaviors),
            new UIPropertyMetadata(null, OnAppendTextChanged));

    public static string GetAppendText(TextBox textBox)
    {
        return (string)textBox.GetValue(AppendTextProperty);
    }

    public static void SetAppendText(
        TextBox textBox,
        string value)
    {
        textBox.SetValue(AppendTextProperty, value);
    }

    private static void OnAppendTextChanged(
        DependencyObject d,
        DependencyPropertyChangedEventArgs args)
    {
        if (args.NewValue == null)
        {
            return;
        }

        string toAppend = args.NewValue.ToString();

        if (toAppend == "")
        {
            return;
        }

        TextBox textBox = d as TextBox;
        textBox?.AppendText(toAppend);
        textBox?.ScrollToEnd();
    }
    #endregion
}

Et cette propriété attachée (pour effacer la case):

public static class TextBoxClearBehavior
{
    public static readonly DependencyProperty TextBoxClearProperty =
        DependencyProperty.RegisterAttached(
            "TextBoxClear",
            typeof(bool),
            typeof(TextBoxClearBehavior),
            new UIPropertyMetadata(false, OnTextBoxClearPropertyChanged));

    public static bool GetTextBoxClear(DependencyObject obj)
    {
        return (bool)obj.GetValue(TextBoxClearProperty);
    }

    public static void SetTextBoxClear(DependencyObject obj, bool value)
    {
        obj.SetValue(TextBoxClearProperty, value);
    }

    private static void OnTextBoxClearPropertyChanged(
        DependencyObject d,
        DependencyPropertyChangedEventArgs args)
    {
        if ((bool)args.NewValue == false)
        {
            return;
        }

        var textBox = (TextBox)d;
        textBox?.Clear();
    }
}   

Ensuite, si vous utilisez un framework d'injection de dépendance tel que MEF, vous pouvez placer tout le code spécifique à la journalisation dans son propre ViewModel:

public interface ILogBoxViewModel
{
    void CmdAppend(string toAppend);
    void CmdClear();

    bool AttachedPropertyClear { get; set; }

    string AttachedPropertyAppend { get; set; }
}

[Export(typeof(ILogBoxViewModel))]
public class LogBoxViewModel : ILogBoxViewModel, INotifyPropertyChanged
{
    private readonly ILog _log = LogManager.GetLogger<LogBoxViewModel>();

    private bool _attachedPropertyClear;
    private string _attachedPropertyAppend;

    public void CmdAppend(string toAppend)
    {
        string toLog = $"{DateTime.Now:HH:mm:ss} - {toAppend}\n";

        // Attached properties only fire on a change. This means it will still work if we publish the same message twice.
        AttachedPropertyAppend = "";
        AttachedPropertyAppend = toLog;

        _log.Info($"Appended to log box: {toAppend}.");
    }

    public void CmdClear()
    {
        AttachedPropertyClear = false;
        AttachedPropertyClear = true;

        _log.Info($"Cleared the GUI log box.");
    }

    public bool AttachedPropertyClear
    {
        get { return _attachedPropertyClear; }
        set { _attachedPropertyClear = value; OnPropertyChanged(); }
    }

    public string AttachedPropertyAppend
    {
        get { return _attachedPropertyAppend; }
        set { _attachedPropertyAppend = value; OnPropertyChanged(); }
    }

    #region INotifyPropertyChanged
    public event PropertyChangedEventHandler PropertyChanged;

    [NotifyPropertyChangedInvocator]
    protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
    #endregion
}

Voici comment ça fonctionne:

  • ViewModel bascule les propriétés attachées pour contrôler la zone de texte.
  • Comme il utilise "Append", il est rapide comme l'éclair.
  • Tout autre ViewModel peut générer des messages de journalisation en appelant des méthodes sur le ViewModel de journalisation.
  • Comme nous utilisons le ScrollViewer intégré à la zone de texte, nous pouvons le faire défiler automatiquement jusqu'au bas de la zone de texte chaque fois qu'un nouveau message est ajouté.
3
Contango

Vous pouvez utiliser

ScrollViewer.HorizontalScrollBarVisibility="Visible"
ScrollViewer.VerticalScrollBarVisibility="Visible"

Ceux-ci sont la propriété attachée de wpf . Pour plus d'informations

http://wpfbugs.blogspot.in/2014/02/wpf-layout-controls-scrollviewer.html

2
ravi saini

J'ai essayé d'obtenir ces suggestions pour travailler pour un bloc de texte, mais ne pouvais pas le faire fonctionner. J'ai même essayé de le faire fonctionner par le concepteur. (Regardez dans Mise en page et développez la liste en cliquant sur la flèche vers le bas "V" en bas) J'ai essayé de régler le scrollviewer sur Visible puis Auto , mais cela ne fonctionnait toujours pas. 

J'ai finalement abandonné et changé la TextBlock en une TextBox avec l'ensemble d'attributsReadonly, et cela a fonctionné comme un charme. 

1
Scott Bordelon

Je ne sais pas si quelqu'un d'autre a ce problème, mais je range ma TextBlock dans une ScrollViewer quelque chose qui a gâché mon interface utilisateur - comme solution de contournement simple, j'ai découvert que le remplacement de la TextBlock par une TextBox comme celle-ci

<TextBox  Name="textBlock" SelectionBrush="Transparent" Cursor="Arrow" IsReadOnly="True" Text="My Text" VerticalScrollBarVisibility="Auto">

crée une TextBox qui ressemble et se comporte comme une TextBlock avec une barre de défilement (et vous pouvez tout faire dans le concepteur).

0
dunkleosteus

C'est une solution simple à cette question. Le défilement vertical ne sera activé que lorsque le texte sera débordé.

<TextBox Text="Try typing some text here " ScrollViewer.VerticalScrollBarVisibility="Auto" TextWrapping="WrapWithOverflow" />

0
Zuhair