2009-07-09 7 views
8

TextWrapping을 "줄 바꾸기"로 설정하면 WPF TextBlock에 여러 줄의 텍스트를 포함 할 수 있습니다. 텍스트 줄 수를 얻는 데 "깨끗한"방법이 있습니까? 원하는 높이를보고 각 높이의 예상 높이로 나누는 것을 고려했습니다. 그러나, 그것은 상당히 더러운 것처럼 보입니다. 더 좋은 방법이 있습니까?TextBlock의 표시 줄 수

답변

8

WPF에 대한 한 가지 중요한 점은 모든 컨트롤이 매우 눈에 띄지 않는다는 것입니다. 이 때문에 우리는 TextBox을 사용할 수 있습니다.이 속성에는 LineCount 속성이 있습니다 (왜 이것이 DependencyProperty가 아니며, 왜 TextBlock에는 모름). TextBox를 사용하면 간단하게 다시 템플릿을 만들 수 있으므로 TextBlock과 유사하게 동작합니다. 사용자 정의 스타일/템플릿에서는 IsEnabled를 False로 설정하고 컨트롤의 기본 템플릿을 다시 만들어 비활성화 된 모양이 더 이상 나타나지 않도록합니다. 또한 TemplateBindings를 사용하여 Background와 같이 유지하려는 모든 속성을 바인딩 할 수 있습니다.

이제
<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> 

, 그것은 우리의 텍스트 상자 모양을 돌봐하고있는 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 
} 

지금, 그것은 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

이것은 훌륭합니다. 그러나 TextBox는 글꼴 모음/글꼴 크기가 동일하므로 TextBlock보다 제한적입니다. 결과적으로 라인 수를 계산하기가 쉽습니다. 반면 TextBlocks는 높이가 다른 인라인을 가질 수 있습니다. 이것은 일을 다소 어렵게 만듭니다. – tom7

-2

간단한 방법은 LineCount 속성입니다. 또한 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

질문은 TextBox가 아니라 TextBlock에 관한 것입니다. – jHilscher

1

나는이 질문은 이미 7 세 것을 봐 왔지만, 난 그냥 솔루션과 함께 제공 :

TextBlock입니다이 LineCount라는 개인 속성이 있습니다. 이 값을 읽는 확장 메서드를 만들었습니다.

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