2009-07-09 11 views
8

Se si imposta TextWrapping su "Wrap", un TextBlock WPF può avere più righe di testo. Esiste un modo "pulito" per ottenere il numero di righe di testo? Ho preso in considerazione l'altezza desiderata e la divisione in base all'altezza stimata di ciascuna linea. Tuttavia, sembra abbastanza sporco. C'è un modo migliore?Conteggio riga visibile di un TextBlock

risposta

8

Una cosa di WPF che è molto bella è che tutti i controlli sono molto privi di vista. Per questo motivo, possiamo fare uso di TextBox, che ha una proprietà LineCount (perché non è una proprietà di dipendenza o perché TextBlock non ce l'ha anche io non lo so). Con il TextBox, possiamo semplicemente ri-stamparlo in modo che si comporti e assomigli più a un TextBlock. Nel nostro Style/Template personalizzato imposteremo IsEnabled su False e creeremo semplicemente un nuovo rimotocollo del controllo in modo che l'aspetto disabilitato non sia più presente. Possiamo anche associare qualsiasi proprietà che vogliamo mantenere, come Sfondo, attraverso l'uso di 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> 

Ora, che si prenderà cura di rendere il nostro sguardo TextBox e comportarsi come un TextBlock, ma come facciamo a ottenere contare la linea?

Bene, se vogliamo accedervi direttamente nel codice sottostante, possiamo registrarci all'evento SizeChanged del TextBox.

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

Tuttavia, dal momento che tendono ad avere bisogno di utilizzare le proprietà del genere in luoghi diversi dalla finestra corrente, e/o sto usando MVVM e non voglio prendere questo approccio, allora possiamo creare qualche AttachedProperties a gestire il recupero e l'impostazione di LineCount. Useremo AttachedProperties per fare la stessa cosa, ma ora saremo in grado di usarlo con qualsiasi TextBox ovunque, e ci legheremo ad esso attraverso il TextBox invece del DataContext di Windows.

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 
} 

Ora, è semplice trovare la 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

Questo è fantastico. Tuttavia, i TextBox sono più limitati di TextBlock perché hanno una famiglia di caratteri e una dimensione di font uniformi. Di conseguenza, è facile calcolare il conteggio delle righe. Al contrario, i blocchi di testo possono avere linee diverse con altezze diverse. Questo rende le cose un po 'più difficili. – tom7

-2

Il modo più semplice è la proprietà LineCount. Inoltre hai un metodo chiamato GetLastVisibleLineIndex che ti consente di sapere quante righe può visualizzare la casella di testo (senza barre di scorrimento).

Se si desidera sapere quando viene aggiunta una linea, è possibile ascoltare l'evento TextChanged e chiedere informazioni sulla proprietà LineCount (sarà necessario mantenere il modulo LineCount in una variabile per effettuare il confronto).

+0

TextBlock non ha una proprietà LineCount. Questo è solo il dominio di TextBox. –

+0

L'ho trovato utile. buone informazioni, risposta sbagliata. – 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 domanda riguardava un TextBlock non un TextBox. – jHilscher

1

ho visto che questa domanda è già 7 anni, ma ho appena venuto con una soluzione:

Il TextBlock avere una proprietà privata chiamata LineCount. Ho creato un metodo di estensione per leggere questo valore:

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); 
    } 
} 
Problemi correlati