Automatische vertikale Bildlaufleiste in WPF TextBlock?

335

Ich habe eine TextBlockin WPF. Ich schreibe viele Zeilen darauf, die seine vertikale Höhe weit überschreiten. Ich habe erwartet, dass in diesem Fall automatisch eine vertikale Bildlaufleiste angezeigt wird, dies ist jedoch nicht der Fall. Ich habe versucht, im Eigenschaftenbereich nach einer Bildlaufleisteneigenschaft zu suchen, konnte jedoch keine finden.

Wie kann ich eine vertikale Bildlaufleiste erstellen, die automatisch für meine erstellt wird, TextBlocksobald der Inhalt die Höhe überschreitet?

Klarstellung: Ich würde es lieber vom Designer machen und nicht direkt an die XAML schreiben.

Bab Yogoo
quelle
1
Beim erneuten Lesen dieser Frage stelle ich fest, dass Sie sie TextBlockzweimal und TextBoxeinmal erwähnen .
Drew Noakes

Antworten:

554

Wickeln Sie es in einen Scroll-Viewer:

<ScrollViewer>
    <TextBlock />
</ScrollViewer>

HINWEIS Diese Antwort gilt für ein TextBlock(schreibgeschütztes Textelement), wie in der ursprünglichen Frage gefordert.

Wenn Sie Bildlaufleisten in einem TextBox(bearbeitbaren Textelement) anzeigen möchten, verwenden Sie die ScrollViewerangehängten Eigenschaften:

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

Gültige Werte für diese beiden Eigenschaften sind Disabled, Auto, Hiddenund Visible.

Drew Noakes
quelle
2
Wie mache ich das vom Designer?
Bab Yogoo
16
Entschuldigung, ich bin mir nicht sicher, ich benutze den WPF-Designer nicht. Ich denke, wenn Sie die XAML direkt hinzufügen, wird sich der Designer selbst aktualisieren.
Drew Noakes
5
@conqenator TextBox.ScrollToEnd ();
Petey B
2
@ Greg, die Frage geht um TextBlocknicht TextBox.
Drew Noakes
7
Manchmal ist eine MaxHeight im Scrollviewer erforderlich, um das Anzeigen des Scolls zu erzwingen, wenn das einschließende Element keine Höhe erzwingt.
HackerBaloo
106

kann jetzt Folgendes verwenden:

<TextBox Name="myTextBox" 
         ScrollViewer.HorizontalScrollBarVisibility="Auto"
         ScrollViewer.VerticalScrollBarVisibility="Auto"
         ScrollViewer.CanContentScroll="True">SOME TEXT
</TextBox>
vince
quelle
19
@jjnguy, ich habe die ursprüngliche Frage so interpretiert, dass sie TextBlocknicht TextBox(wie im Titel und in der Eröffnungszeile), sondern im zweiten Absatz erwähnt wird TextBox. Um klar zu sein, ist diese Antwort auf jeden Fall der beste Ansatz für die Textbox , und ich ist das Beste , was ich kenne für Textblocks :)
Drew Noakes
@ Draw, ah, macht Sinn. Danke für die Klarstellung.
jjnguy
2
Hat auch bei mir besser funktioniert. Zumindest für eine TextBox verschwinden bei Verwendung des ScrollViewer wie in der akzeptierten Antwort die Ränder der TextBox, da das gesamte Steuerelement gescrollt wird und nicht nur der Inhalt.
Betankt am
20

Etwas besseres wäre:

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

Dadurch wird sichergestellt, dass der Text in Ihrem Textblock nicht überläuft und die Elemente unter dem Textblock überlappt, wie dies der Fall sein kann, wenn Sie das Raster nicht verwenden. Das ist mir passiert, als ich andere Lösungen ausprobiert habe, obwohl sich der Textblock bereits in einem Raster mit anderen Elementen befand. Beachten Sie, dass die Breite des Textblocks Auto sein sollte und Sie das gewünschte mit im Rasterelement angeben sollten. Ich habe das in meinem Code gemacht und es funktioniert wunderbar. HTH.

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

Auf diese Weise können Sie die scrollende TextBox in XAML verwenden und als Textbereich verwenden.

John
quelle
1
Die Frage bezieht sich TextBlocknicht auf TextBox.
Afzaal Ahmad Zeeshan
Nicht ganz richtige Antwort, aber ich fand VerticalScrollBarVisibility ein nützlicher Hinweis, also +1
Malachi
4

Diese Antwort beschreibt eine Lösung mit MVVM.

Diese Lösung eignet sich hervorragend, wenn Sie einem Fenster ein Protokollierungsfeld hinzufügen möchten, das bei jedem Hinzufügen einer neuen Protokollierungsnachricht automatisch nach unten gescrollt wird.

Sobald diese angehängten Eigenschaften hinzugefügt wurden, können sie überall wiederverwendet werden, sodass eine sehr modulare und wiederverwendbare Software entsteht.

Fügen Sie diese XAML hinzu:

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

Fügen Sie diese angehängte Eigenschaft hinzu:

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
}

Und diese angehängte Eigenschaft (um die Box zu löschen):

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

Wenn Sie dann ein Abhängigkeitsinjektionsframework wie MEF verwenden, können Sie den gesamten protokollspezifischen Code in einem eigenen ViewModel ablegen:

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
}

So funktioniert das:

  • Das ViewModel schaltet die angehängten Eigenschaften um, um die TextBox zu steuern.
  • Da es "Anhängen" verwendet, ist es blitzschnell.
  • Jedes andere ViewModel kann Protokollierungsnachrichten generieren, indem Methoden im Protokollierungs-ViewModel aufgerufen werden.
  • Wenn wir den in die TextBox integrierten ScrollViewer verwenden, können wir ihn jedes Mal, wenn eine neue Nachricht hinzugefügt wird, automatisch zum unteren Rand des Textfelds scrollen lassen.
Contango
quelle
4
<ScrollViewer MaxHeight="50"  
              Width="Auto" 
              HorizontalScrollBarVisibility="Disabled"
              VerticalScrollBarVisibility="Auto">
     <TextBlock Text="{Binding Path=}" 
                Style="{StaticResource TextStyle_Data}" 
                TextWrapping="Wrap" />
</ScrollViewer>

Ich mache das auf eine andere Weise, indem ich MaxHeight in ScrollViewer einsetze.

Passen Sie einfach die MaxHeight an, um mehr oder weniger Textzeilen anzuzeigen. Einfach.

Tony Wu
quelle
1

Ich habe versucht, diese Vorschläge für einen Textblock zum Laufen zu bringen, konnte sie jedoch nicht zum Laufen bringen. Ich habe sogar versucht, es vom Designer zum Laufen zu bringen. (Schauen Sie im Layout nach und erweitern Sie die Liste, indem Sie unten auf den Abwärtspfeil "V" klicken.) Ich habe versucht, den Bildlauf-Viewer auf Sichtbar und dann auf Auto einzustellen , aber es wäre immer noch nicht.

Ich gab schließlich auf und änderte das TextBlockzu einem TextBoxmit dem Readonly- Attributsatz, und es funktionierte wie ein Zauber.

Scott Bordelon
quelle
0

Ich weiß nicht, ob jemand anderes dieses Problem hat, aber ich habe es TextBlockin eine ScrollViewerirgendwie durcheinandergebrachte Benutzeroberfläche eingepackt - als einfache Problemumgehung habe ich herausgefunden, dass das TextBlockdurch ein TextBoxähnliches ersetzt wird

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

Erstellt eine TextBox, die aussieht und sich wie eine TextBlockmit einer Bildlaufleiste verhält (und Sie können alles im Designer tun).

Dunklerosteus
quelle
0

Dies ist eine einfache Lösung für diese Frage. Der vertikale Bildlauf wird nur aktiviert, wenn der Text überläuft.

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

Zuhair
quelle