Barra di scorrimento verticale automatica in WPF TextBlock?


336

Ho un TextBlockin WPF. Vi scrivo molte righe, superando di gran lunga la sua altezza verticale. Mi aspettavo che una barra di scorrimento verticale apparisse automaticamente quando ciò accade, ma non è così. Ho provato a cercare una proprietà della barra di scorrimento nel riquadro Proprietà, ma non sono riuscita a trovarne una.

Come posso fare in modo che la barra di scorrimento verticale venga creata automaticamente per il mio TextBlockquando il suo contenuto supera la sua altezza?

Chiarimento: preferirei farlo dal designer e non scrivendo direttamente su XAML.


1
Rileggendo questa domanda, noto che menzionate TextBlockdue volte e TextBoxuna volta.
Ha disegnato Noakes il

Risposte:


555

Avvolgilo in un visualizzatore di scorrimento:

<ScrollViewer>
    <TextBlock />
</ScrollViewer>

NOTA questa risposta si applica a un TextBlock(un elemento di testo di sola lettura) come richiesto nella domanda originale.

Se vuoi mostrare le barre di scorrimento in un TextBox(un elemento di testo modificabile), usa le ScrollViewerproprietà associate:

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

I valori validi per queste due proprietà sono Disabled, Auto, Hiddene Visible.


2
Come posso farlo dal designer?
Bab Yogoo,

16
Mi dispiace non sono sicuro, non uso il designer WPF. Penso che se aggiungi direttamente XAML, il designer si aggiornerà da solo.
Drew Noakes,

5
@conqenator TextBox.ScrollToEnd ();
Petey B,

2
@Greg, la domanda TextBlocknon riguarda TextBox.
Ha disegnato Noakes il

7
A volte è necessario un MaxHeight su Scrollviewer per forzare lo scoll ad apparire se l'elemento racchiuso non impone alcuna altezza.
HackerBaloo,

106

puoi usare ora quanto segue:

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

19
@jjnguy, ho interpretato la domanda originale come se TextBlocknon fosse TextBox(come nel titolo e nella riga iniziale), ma il secondo paragrafo menzionato TextBox. Per essere chiari, questa risposta è sicuramente l'approccio migliore per le caselle di testo e la mia è la migliore che io conosca per i blocchi di testo :)
Drew Noakes

@Drew, ah, ha senso. Grazie per il chiarimento.
jjnguy,

2
Ha funzionato meglio anche per me. Almeno per un TextBox, quando si usa lo ScrollViewer attorno ad esso, come nella risposta accettata, i bordi del TextBox scompaiono, perché l'intero controllo viene fatto scorrere, e non solo il suo contenuto.
Alimentato il

20

Qualcosa di meglio sarebbe:

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

Ciò si assicura che il testo nel blocco di testo non si sovrapponga e si sovrapponga agli elementi sotto il blocco di testo, come potrebbe accadere se non si utilizza la griglia. Mi è successo quando ho provato altre soluzioni anche se il blocco di testo era già in una griglia con altri elementi. Tieni presente che la larghezza del blocco di testo deve essere Auto e devi specificare il desiderato con nell'elemento Griglia. L'ho fatto nel mio codice e funziona magnificamente. HTH.


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

Questo è il modo di usare la TextBox a scorrimento in XAML e usarla come area di testo.


1
La questione è legata alla TextBlocknon TextBox.
Afzaal Ahmad Zeeshan,

Risposta non del tutto corretta, ma ho trovato VerticalScrollBarVisibility un suggerimento utile quindi +1
Malachi,

4

Questa risposta descrive una soluzione che utilizza MVVM.

Questa soluzione è ottima se si desidera aggiungere una finestra di registrazione a una finestra, che scorre automaticamente verso il basso ogni volta che viene aggiunto un nuovo messaggio di registrazione.

Una volta aggiunte queste proprietà associate, possono essere riutilizzate ovunque, quindi rende il software molto modulare e riutilizzabile.

Aggiungi questo 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">

Aggiungi questa proprietà collegata:

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
}

E questa proprietà collegata (per cancellare la casella):

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

Quindi, se si utilizza un framework di iniezione delle dipendenze come MEF, è possibile inserire tutto il codice specifico della registrazione nel proprio 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
}

Ecco come funziona:

  • ViewModel attiva o disattiva le proprietà associate per controllare TextBox.
  • Poiché utilizza "Aggiungi", è velocissimo.
  • Qualsiasi altro ViewModel può generare messaggi di registrazione chiamando i metodi sul ViewModel di registrazione.
  • Quando utilizziamo ScrollViewer incorporato in TextBox, possiamo farlo scorrere automaticamente fino alla fine della casella di testo ogni volta che viene aggiunto un nuovo messaggio.

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

Lo sto facendo in un altro modo inserendo MaxHeight in ScrollViewer.

Basta regolare MaxHeight per mostrare più o meno righe di testo. Facile.



1

Ho cercato di far funzionare questi suggerimenti per un blocco di testo, ma non sono riuscito a farlo funzionare. Ho anche provato a farlo funzionare dal designer. (Cerca in Layout ed espandi l'elenco facendo clic sulla freccia rivolta verso il basso "V" in basso) Ho provato a impostare il visualizzatore di scorrimento su Visibile e quindi su Auto , ma non funzionava ancora.

Alla fine ho rinunciato e l'ho cambiato TextBlockin TextBoxcon l' attributo Readonly impostato, e ha funzionato come un incantesimo.


0

Non so se qualcun altro ha questo problema ma avvolgendomi TextBlockin un modo in cui ScrollViewerho incasinato la mia interfaccia utente - come semplice soluzione ho capito che la sostituzione TextBlockcon una TextBoxcome questa

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

crea un oggetto TextBoxche si presenta e si comporta come un TextBlockcon una barra di scorrimento (e puoi fare tutto nella finestra di progettazione).


0

Questa è una soluzione semplice a questa domanda. Lo scorrimento verticale verrà attivato solo quando il testo trabocca.

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

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.