2009-07-09 78 views
8

TextWrappingを "Wrap"に設定すると、WPF TextBlockに複数行のテキストを含めることができます。 テキスト行数を取得する「クリーン」な方法はありますか?私は、希望の高さを見て、それを各行の推定高さで割ることを検討しました。しかし、それはかなり汚れているようです。より良い方法がありますか?TextBlockの表示行数

答えて

8

WPFについてのすばらしい点の1つは、すべてのコントロールが非常に見苦しいことです。このため、TextBoxを使用することができます。これはLineCountプロパティを持っています(なぜそれがDependencyPropertyでないのか、またはTextBlockにはわからないのはなぜですか)。 TextBoxを使うと、それを再テンプレートして、それが動作し、TextBlockのように見えるようにすることができます。私たちのカスタムスタイル/テンプレートでは、IsEnabledをFalseに設定し、コントロールの基本的な再テンプレートを作成して、無効な外観がなくなるようにします。 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> 

さて、それは私たちのTextBoxの外観を作るの世話をし、TextBlockのように振る舞うが、どのように我々は、ラインカウント入手できますでしょうか?

私たちがコードの中で直接アクセスしたいのであれば、TextBoxのSizeChangedイベントに登録することができます。

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

、私は現在のウィンドウその後、他の場所でそのようなプロパティを使用する必要がする傾向があり、および/またはMVVMを使用していますが、そのアプローチを取ることにしたくないし、我々はいくつかのAttachedPropertiesを作成することができますので、 LineCountの取得と設定を処理します。 AttachedPropertiesを使用して同じことを行うつもりですが、今はどこのTextBoxでも使用でき、WindowのDataContextではなくTextBoxを通じてバインドできます。

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 
} 

さて、それはラインカウントを見つけることは簡単です:

<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

これは素晴らしいです。 しかし、TextBoxは、一様なフォントファミリ/フォントサイズを持つため、TextBlockよりも制限があります。その結果、行数を計算するのは簡単です。一方、TextBlocksは異なる高さで異なるインラインを持つことができます。これは物事をやや難しくしています。 – tom7

-2

簡単な方法は、ラインカウントプロパティです。また、GetLastVisibleLineIndexというメソッドがあり、テキストボックスが(スクロールバーなしで)表示できる行数を知ることができます。

行が追加されたことを知りたい場合は、TextChangedイベントで聞くことができ、LineCountプロパティについて問い合わせることができます(比較のためには、las LineCountを変数に保持する必要があります)。

+0

TextBlockにはLineCountプロパティがありません。これは、TextBoxのドメインだけです。 –

+0

私はそれが有用であることを発見した。良い情報、間違った答え。 – 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

質問はテキストボックスではなくTextBlockに関するものでした。 – jHilscher

1

私はこの質問はすでに7歳であることを見てきましたが、私はちょうどソリューションに付属している:

のTextBlockがラインカウントと呼ばれる私有財産を持っています。この値を読み取る拡張メソッドを作成しました:

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); 
    } 
} 
関連する問題