2009-07-09 16 views
8

की दृश्यमान रेखा गणना यदि आप टेक्स्ट रैपिंग को "लपेटें" पर सेट करते हैं, तो एक WPF टेक्स्टब्लॉक में टेक्स्ट की कई पंक्तियां हो सकती हैं। क्या टेक्स्ट की लाइनों की संख्या प्राप्त करने के लिए कोई "साफ" तरीका है? मैंने वांछित ऊंचाई को देखने और प्रत्येक पंक्ति की अनुमानित ऊंचाई से इसे विभाजित करने पर विचार किया। हालांकि, यह काफी गंदा लगता है। क्या कोई बेहतर तरीका है?टेक्स्टब्लॉक

उत्तर

8

डब्ल्यूपीएफ के बारे में एक बात यह बहुत अच्छी है कि सभी नियंत्रण बहुत लापरवाह हैं। इस वजह से, हम TextBox का उपयोग कर सकते हैं, जिसमें लाइनकाउंट प्रॉपर्टी है (क्यों यह निर्भरता प्रॉपर्टी नहीं है या टेक्स्टब्लॉक में यह क्यों नहीं है जो मुझे नहीं पता)। टेक्स्टबॉक्स के साथ, हम इसे फिर से टेम्पलेट कर सकते हैं, इसलिए यह व्यवहार करता है और टेक्स्टब्लॉक की तरह दिखता है। हमारे कस्टम स्टाइल/टेम्पलेट में हम IsEnabled को गलत पर सेट करने जा रहे हैं, और केवल नियंत्रण के मूल पुन: टेम्पलेटिंग को बनाएंगे ताकि अक्षम दिखने वाला न हो। हम टेम्पलेट बाइंडिंग के उपयोग के माध्यम से पृष्ठभूमि की तरह किसी भी संपत्ति को बनाए रखना चाहते हैं।

<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 तरह व्यवहार करते हैं, लेकिन हम कैसे लाइन गिनती मिलता है?

अच्छा, अगर हम इसे सीधे कोड में एक्सेस करना चाहते हैं तो हम टेक्स्टबॉक्स के साइज चेंजेड इवेंट में पंजीकरण कर सकते हैं।

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 बना सकते हैं लाइनकाउंट की पुनर्प्राप्ति और सेटिंग को संभालें। हम एक ही काम करने के लिए अटैचडप्रॉपर्टीज का उपयोग करने जा रहे हैं, लेकिन अब हम इसे किसी भी टेक्स्टबॉक्स के साथ कहीं भी इस्तेमाल करने में सक्षम होंगे, और विंडो के डेटाकॉन्टेक्स्ट के बजाय उस टेक्स्टबॉक्स के माध्यम से इसे बांध सकते हैं।

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

यह बहुत अच्छा है। हालांकि, टेक्स्टबॉक्स टेक्स्टब्लॉक से अधिक सीमित है क्योंकि उनके पास एक समान फ़ॉन्ट परिवार/फ़ॉन्ट आकार है। नतीजतन, लाइन गिनती की गणना करना आसान है। दूसरी तरफ टेक्स्टब्लॉक अलग-अलग ऊंचाइयों के साथ अलग-अलग रूपरेखा रख सकते हैं। यह चीजों को कुछ और मुश्किल बनाता है। – tom7

-2

आसान तरीका LineCount संपत्ति है। इसके अलावा आपके पास GetLastVisibleLineIndex नामक एक विधि है जो आपको बताती है कि टेक्स्टबॉक्स कितनी लाइनें प्रदर्शित कर सकता है (स्क्रॉल बार के बिना)।

यदि आप जानना चाहते हैं कि कोई लाइन कब जोड़ा जाता है तो आप टेक्स्ट चेंजेड ईवेंट में सुन सकते हैं और लाइनकाउंट प्रॉपर्टी के बारे में पूछ सकते हैं (आपको तुलना करने के लिए लास लाइनकाउंट को एक चर में रखना होगा)।

+0

टेक्स्टब्लॉक में लाइनकाउंट संपत्ति नहीं है। यह केवल टेक्स्टबॉक्स का डोमेन है। –

+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

प्रश्न टेक्स्टब्लॉक के बारे में टेक्स्टब्लॉक नहीं था। – 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); 
    } 
} 
संबंधित मुद्दे