2009-07-28 24 views
265

Tengo un TextBlock en WPF. Le escribo muchas líneas, que exceden por mucho su altura vertical. Esperaba que una barra de desplazamiento vertical apareciera automáticamente cuando eso sucediera, pero no fue así. Traté de buscar una propiedad de la barra de desplazamiento en el panel Propiedades, pero no pude encontrar una.Barra de desplazamiento vertical automática en WPF TextBlock?

¿Cómo puedo hacer la barra de desplazamiento vertical creada automáticamente para mi TextBlock una vez que su contenido exceda su altura?

Aclaración: Prefiero hacerlo desde el diseñador y no escribiendo directamente al XAML.

+0

Al volver a leer esta pregunta, noto que menciona 'TextBlock' dos veces y' TextBox' una vez. –

Respuesta

446

envolverlo en un visor de desplazamiento:

<ScrollViewer> 
    <TextBlock /> 
</ScrollViewer> 

NOTA esta respuesta se aplica a una (un elemento de texto de sólo lectura) TextBlock como solicitó en la pregunta original.

Si desea mostrar las barras de desplazamiento en un (elemento de texto editable) TextBox continuación, utilizar el ScrollViewer propiedades adjuntas:

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

Los valores válidos para estas dos propiedades son Disabled, Auto, Hidden y Visible.

+2

¿Cómo lo hago desde el diseñador? –

+14

Lo siento, no estoy seguro, no uso el diseñador de WPF. Creo que si agrega el XAML directamente, el diseñador se actualizará solo. –

+5

@conqenator TextBox.ScrollToEnd(); –

89

puede utilizar el siguiente ahora:

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

Gracias! Esto funcionó para mí mejor que la respuesta anterior. – jjnguy

+14

@jjnguy, interpreté que la pregunta original era sobre 'TextBlock' no' TextBox' (como en el título y la línea de apertura), pero el segundo párrafo menciona 'TextBox'. Para ser claros, esta respuesta es definitivamente el mejor enfoque para las _cajas_ de texto, y la mía es la mejor que conozco para el texto _blocks_ :) –

+0

@Drew, ah, tiene sentido. Gracias por la aclaración. – jjnguy

18

Algo mejor sería:

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

Esto asegura que el texto en el bloque de texto no se desborde y se superponen los elementos por debajo del bloque de texto que puede ser el caso si no usas la grilla. Eso me sucedió cuando probé otras soluciones, aunque el bloque de texto ya estaba en una cuadrícula con otros elementos. Tenga en cuenta que el ancho del bloque de texto debe ser Automático y debe especificar el deseado con en el elemento Cuadrícula. Hice esto en mi código y funciona muy bien. HTH.

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

Esta es una forma de utilizar TextBox desplazable en XAML y utilizarlo como área de texto.

+1

La pregunta está relacionada con 'TextBlock' no' TextBox'. –

+0

Respuesta no del todo correcta, pero encontré que VerticalScrollBarVisibility es una sugerencia útil para que +1 – Malachi

3

Esta respuesta describe una solución usando MVVM.

Esta solución es excelente si desea agregar un cuadro de registro a una ventana, que se desplaza automáticamente hacia abajo cada vez que se agrega un nuevo mensaje de registro.

Una vez que se agregan estas propiedades adjuntas, se pueden reutilizar en cualquier lugar, por lo que es un software muy modular y reutilizable.

Añadir este 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"> 

Añadir esta propiedad adjunta:

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 
} 

Y esta propiedad adjunta (para desactivar la casilla):

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

Entonces, si usted' Usando un marco de inyección de dependencia como MEF, puede colocar todo el código específico de registro en su propio modelo de vista:

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 
} 

Así es como funciona:

  • El modelo de vista cambia las propiedades asociadas para controlar el cuadro de texto.
  • Como está usando "Append", es muy rápido.
  • Cualquier otro ViewModel puede generar mensajes de registro llamando a métodos en el modelo de registro ViewModel.
  • Al usar el ScrollViewer integrado en el TextBox, podemos hacer que se desplace automáticamente a la parte inferior del cuadro de texto cada vez que se agrega un nuevo mensaje.
3
<ScrollViewer MaxHeight="50" 
       Width="Auto" 
       HorizontalScrollBarVisibility="Disabled" 
       VerticalScrollBarVisibility="Auto"> 
    <TextBlock Text="{Binding Path=}" 
       Style="{StaticResource TextStyle_Data}" 
       TextWrapping="Wrap" /> 
</ScrollViewer> 

que estoy haciendo esto de otra manera poniendo en MaxHeight ScrollViewer.

Simplemente ajuste MaxHeight para mostrar más o menos líneas de texto. Fácil.

Cuestiones relacionadas