2009-07-09 11 views
8

Si establece TextWrapping de "envolver", un WPF TextBlock puede tener varias líneas de texto. ¿Existe una forma "limpia" de obtener el número de líneas de texto? Consideré mirar la altura deseada y dividirla por una altura estimada de cada línea. Sin embargo, eso parece bastante sucio. ¿Hay una mejor manera?número de líneas visibles de un TextBlock

Respuesta

8

Una cosa acerca de WPF que es muy interesante es que todos los controles son muy lookless. Debido a esto, podemos hacer uso de TextBox, que tiene una propiedad LineCount (Por qué no es una DependencyProperty o por qué TextBlock no la tiene también, no lo sé). Con TextBox, podemos simplemente volver a crear una plantilla para que se comporte y se parezca más a un TextBlock. En nuestro Estilo/Plantilla personalizado, vamos a configurar IsEnabled en False, y simplemente crearemos una nueva plantilla básica del control para que la apariencia deshabilitada ya no esté presente. También podemos enlazar cualquier propiedad que deseemos mantener, como Fondo, mediante el uso de TemplateBindings.

<Style x:Key="Local_TextBox" 
    TargetType="{x:Type TextBoxBase}"> 
    <Setter Property="IsEnabled" 
      Value="False" /> 
    <Setter Property="Template"> 
     <Setter.Value> 
      <ControlTemplate TargetType="{x:Type TextBoxBase}"> 
       <Border Name="Border" 
        Background="{TemplateBinding Background}"> 
        <ScrollViewer x:Name="PART_ContentHost" /> 
       </Border> 
      </ControlTemplate> 
     </Setter.Value> 
</Setter> 
</Style> 

Ahora, que se encargará de hacer nuestra mirada cuadro de texto y comportarse como un TextBlock, pero ¿cómo obtener el recuento de la línea?

Bueno, si queremos acceder a él directamente en el código detrás entonces podemos registrar a SizeChanged Evento del cuadro de texto.

public partial class MainWindow : Window 
{ 
    public MainWindow() 
    { 
     InitializeComponent(); 

     LongText = "This is a long line that has lots of text in it. Because it is a long line, if a TextBlock's TextWrapping property is set to wrap then the text will wrap onto new lines. However, we can also use wrapping on a TextBox, that has some diffrent properties availible and then re-template it to look just like a TextBlock!"; 

     uiTextBox.SizeChanged += new SizeChangedEventHandler(uiTextBox_SizeChanged); 

     this.DataContext = this; 
    } 

    void uiTextBox_SizeChanged(object sender, SizeChangedEventArgs e) 
    { 
     Lines = uiTextBox.LineCount; 
    } 

    public string LongText { get; set; } 

    public int Lines 
    { 
     get { return (int)GetValue(LinesProperty); } 
     set { SetValue(LinesProperty, value); } 
    } 

    // Using a DependencyProperty as the backing store for Lines. This enables animation, styling, binding, etc... 
    public static readonly DependencyProperty LinesProperty = 
     DependencyProperty.Register("Lines", typeof(int), typeof(MainWindow), new UIPropertyMetadata(-1)); 
} 

Sin embargo, ya que tienden a tener que utilizar las propiedades de ese tipo en lugares distintos de la ventana actual, y/o estoy usando MVVM y no quiero tomar ese enfoque, entonces podemos crear algunos AttachedProperties a manejar la recuperación y configuración de LineCount. Vamos a usar AttachedProperties para hacer lo mismo, pero ahora podremos usarlo con cualquier TextBox en cualquier lugar, y enlazarnos a través de ese TextBox en lugar del DataContext de la ventana.

public class AttachedProperties 
{ 
    #region BindableLineCount AttachedProperty 
    public static int GetBindableLineCount(DependencyObject obj) 
    { 
     return (int)obj.GetValue(BindableLineCountProperty); 
    } 

    public static void SetBindableLineCount(DependencyObject obj, int value) 
    { 
     obj.SetValue(BindableLineCountProperty, value); 
    } 

    // Using a DependencyProperty as the backing store for BindableLineCount. This enables animation, styling, binding, etc... 
    public static readonly DependencyProperty BindableLineCountProperty = 
     DependencyProperty.RegisterAttached(
     "BindableLineCount", 
     typeof(int), 
     typeof(MainWindow), 
     new UIPropertyMetadata(-1)); 

    #endregion // BindableLineCount AttachedProperty 

    #region HasBindableLineCount AttachedProperty 
    public static bool GetHasBindableLineCount(DependencyObject obj) 
    { 
     return (bool)obj.GetValue(HasBindableLineCountProperty); 
    } 

    public static void SetHasBindableLineCount(DependencyObject obj, bool value) 
    { 
     obj.SetValue(HasBindableLineCountProperty, value); 
    } 

    // Using a DependencyProperty as the backing store for HasBindableLineCount. This enables animation, styling, binding, etc... 
    public static readonly DependencyProperty HasBindableLineCountProperty = 
     DependencyProperty.RegisterAttached(
     "HasBindableLineCount", 
     typeof(bool), 
     typeof(MainWindow), 
     new UIPropertyMetadata(
      false, 
      new PropertyChangedCallback(OnHasBindableLineCountChanged))); 

    private static void OnHasBindableLineCountChanged(DependencyObject o, DependencyPropertyChangedEventArgs e) 
    { 
     var textBox = (TextBox)o; 
     if ((e.NewValue as bool?) == true) 
     { 
      textBox.SetValue(BindableLineCountProperty, textBox.LineCount); 
      textBox.SizeChanged += new SizeChangedEventHandler(box_SizeChanged); 
     } 
     else 
     { 
      textBox.SizeChanged -= new SizeChangedEventHandler(box_SizeChanged); 
     } 
    } 

    static void box_SizeChanged(object sender, SizeChangedEventArgs e) 
    { 
     var textBox = (TextBox)sender; 
     (textBox).SetValue(BindableLineCountProperty, (textBox).LineCount); 
    } 
    #endregion // HasBindableLineCount AttachedProperty 
} 

Ahora, es fácil de encontrar el LineCount:

<StackPanel> 
    <TextBox x:Name="uiTextBox" 
      TextWrapping="Wrap" 
      local:AttachedProperties.HasBindableLineCount="True" 
      Text="{Binding LongText}" 
      Style="{StaticResource Local_TextBox}" /> 

    <TextBlock Text="{Binding Lines, StringFormat=Binding through the code behind: {0}}" /> 
    <TextBlock Text="{Binding ElementName=uiTextBox, Path=(local:AttachedProperties.BindableLineCount), StringFormat=Binding through AttachedProperties: {0}}" /> 
</StackPanel> 
+0

Esto es genial. Sin embargo, TextBox son más limitados que TextBlock porque tienen una familia de fuentes/tamaño de fuente uniformes. Como resultado, es fácil calcular el número de líneas. TextBlocks por otro lado puede tener diferentes líneas con diferentes alturas. Esto hace las cosas un poco más difíciles. – tom7

-2

La forma más sencilla es la propiedad LineCount. También tiene un método llamado GetLastVisibleLineIndex que le permite saber cuántas líneas puede mostrar el cuadro de texto (sin barras de desplazamiento).

Si desea saber cuándo se agrega una línea, puede escuchar en el evento TextChanged y preguntar acerca de la propiedad LineCount (deberá mantener el lasCuenta de línea en una variable para hacer la comparación).

+0

TextBlock no tiene una propiedad LineCount. Ese es solo el dominio de TextBox. –

+0

Me pareció útil tho. buena información, respuesta incorrecta. – mdw7326

3
// this seems to do the job   

<TextBox x:Name="DescriptionTextBox" 
         Grid.Row="03" 
         Grid.RowSpan="3" 
         Grid.Column="01" 
         Width="100" 
         AcceptsReturn="True" 
         MaxLength="100" 
         MaxLines="3" 
         PreviewKeyDown="DescriptionTextBox_PreviewKeyDown" 
         Text="{Binding Path=Description, 
             Mode=TwoWay, 
             UpdateSourceTrigger=PropertyChanged}" 
         TextWrapping="Wrap" /> 



     /// <summary> 
     /// we need to limit a multi line textbox at entry time 
     /// </summary> 
     /// <param name="sender"> 
     /// The sender. 
     /// </param> 
     /// <param name="e"> 
     /// The e. 
     /// </param> 
     private void DescriptionTextBox_PreviewKeyDown(object sender, System.Windows.Input.KeyEventArgs e) 
     { 
      TextBox thisTextBox = sender as TextBox; 
      if (thisTextBox != null) 
      { 
       // only check if we have passed the MaxLines 
       if (thisTextBox.LineCount > thisTextBox.MaxLines) 
       { 
        // we are going to discard the last entered character 
        int numChars = thisTextBox.Text.Length; 

        // force the issue 
        thisTextBox.Text = thisTextBox.Text.Substring(0, numChars - 1); 

        // set the cursor back to the last allowable character 
        thisTextBox.SelectionStart = numChars - 1; 

        // disallow the key being passed in 
        e.Handled = true; 
       } 
      } 
     } 
+0

La pregunta era acerca de un TextBlock no TextBox. – jHilscher

1

he visto que esta cuestión ya es de 7 años de edad, pero me acaba de llegar con una solución:

El TextBlock tienen una propiedad privada llamada LineCount. Creé un método de extensión para leer este valor:

public static class TextBlockExtension 
{ 
    public static int GetLineCount(this TextBlock tb) 
    { 
     var propertyInfo = GetPrivatePropertyInfo(typeof(TextBlock), "LineCount"); 
     var result = (int)propertyInfo.GetValue(tb); 
     return result; 
    } 

    private static PropertyInfo GetPrivatePropertyInfo(Type type, string propertyName) 
    { 
     var props = type.GetProperties(BindingFlags.Instance | BindingFlags.GetProperty | BindingFlags.NonPublic); 
     return props.FirstOrDefault(propInfo => propInfo.Name == propertyName); 
    } 
} 
Cuestiones relacionadas