Limit number of lines entered in WPF textbox

Jay

I am trying to limit the number of lines a user can enter in a textbox.

I have been researching - the closest I can find is this: Limit the max number of chars per line in a textbox .

And Limit the max number of chars per line in a textbox which turns out to be for winforms.

This isn't quite what I'm after... also worth mentioning that there is a misleading maxlines property which I have discovered only limits what is shown in the text box.

My requirements:

  • Do not require the use of a mono-spaced font
  • Limit the textbox to having a maximum of 5 lines
  • Accepts carriage return
  • Do not allow extra carriage returns
  • Stops text input when max length has been reached
  • Wraps text (don't particularly care if it does this in-between words or breaks up whole words)
  • Handles text being pasted into the control and will only paste in what will fit.
  • No scroll bars
  • Also - and this would nice - having the option of limiting the number of characters per line

These requirements are for creating a WYSIWYG textbox which will be used for capturing data that will eventually be printed, and the fonts need to be changeable - if the text gets cut off or is too big for a fixed size line - then it will come out that way in print (even if it does not look right).

I've had a stab at doing this myself by handling events - but am having a great deal of trouble getting this right. Here is my code so far.

XAML

 <TextBox TextWrapping="Wrap" AcceptsReturn="True"
        PreviewTextInput="UIElement_OnPreviewTextInput"
        TextChanged="TextBoxBase_OnTextChanged" />

Code Behind

 public int TextBoxMaxAllowedLines { get; set; }
    public int TextBoxMaxAllowedCharactersPerLine { get; set; }


    public MainWindow()
    {
        InitializeComponent();

        TextBoxMaxAllowedLines = 5;
        TextBoxMaxAllowedCharactersPerLine = 50;
    }

    private void TextBoxBase_OnTextChanged(object sender, TextChangedEventArgs e)
    {
        TextBox textBox = (TextBox)sender;

        int textLineCount = textBox.LineCount;

        if (textLineCount > TextBoxMaxAllowedLines)
        {
            StringBuilder text = new StringBuilder();
            for (int i = 0; i < TextBoxMaxAllowedLines; i++)
                text.Append(textBox.GetLineText(i));

            textBox.Text = text.ToString();
        }

    }

    private void UIElement_OnPreviewTextInput(object sender, TextCompositionEventArgs e)
    {
        TextBox textBox = (TextBox)sender;

        int textLineCount = textBox.LineCount;


        for (int i = 0; i < textLineCount; i++)
        {
            var line = textBox.GetLineText(i);

            if (i == TextBoxMaxAllowedLines-1)
            {
                int selectStart = textBox.SelectionStart;
                textBox.Text = textBox.Text.TrimEnd('\r', '\n');
                textBox.SelectionStart = selectStart;

                //Last line
                if (line.Length > TextBoxMaxAllowedCharactersPerLine)
                    e.Handled = true;
            }
            else
            {
                if (line.Length > TextBoxMaxAllowedCharactersPerLine-1 && !line.EndsWith("\r\n"))
                    e.Handled = true;    
            }

        }
    }

This doesn't quite work right - I am getting strange behaviour on the last line and the selected position within the textbox keeps jumping about.

As an aside, maybe I am going down the wrong track... I was also wondering if this could be achieved by using a regular expression using something like this: https://stackoverflow.com/a/1103822/685341

I am open to any ideas as I have been struggling with this for a while. The requirements listed above are immutable - I am unable to change them.

Jay

Here is my final solution - I'd still like to hear if anyone can come up with a better way of doing this...

This just handles max number of lines - I haven't done anything with max characters yet - but it's logically a simple extension to what I have already done.

As I'm handling the textChanged event of the textbox - this also covers pasing into the control too - I haven't found a clean way to truncate text in this event (unless I handle the key_preview separately) - so I'm just not allowing invalid input by undoing.

XAML

<TextBox TextWrapping="Wrap" AcceptsReturn="True" 
             HorizontalScrollBarVisibility="Disabled" VerticalScrollBarVisibility="Disabled">
       <i:Interaction.Behaviors>
            <lineLimitingTextBoxWpfTest:LineLimitingBehavior TextBoxMaxAllowedLines="5" />

        </i:Interaction.Behaviors>
    </TextBox>

Code (for behaviour)

/// <summary> limits the number of lines the textbox will accept </summary>
public class LineLimitingBehavior : Behavior<TextBox>
{
    /// <summary> The maximum number of lines the textbox will allow </summary>
    public int? TextBoxMaxAllowedLines { get; set; }

    /// <summary>
    /// Called after the behavior is attached to an AssociatedObject.
    /// </summary>
    /// <remarks>
    /// Override this to hook up functionality to the AssociatedObject.
    /// </remarks>
    protected override void OnAttached()
    {
        if (TextBoxMaxAllowedLines != null && TextBoxMaxAllowedLines > 0)
            AssociatedObject.TextChanged += OnTextBoxTextChanged;
    }

    /// <summary>
    /// Called when the behavior is being detached from its AssociatedObject, but before it has actually occurred.
    /// </summary>
    /// <remarks>
    /// Override this to unhook functionality from the AssociatedObject.
    /// </remarks>
    protected override void OnDetaching()
    {
        AssociatedObject.TextChanged -= OnTextBoxTextChanged;
    }

    private void OnTextBoxTextChanged(object sender, TextChangedEventArgs e)
    {
        TextBox textBox = (TextBox)sender;

        int textLineCount = textBox.LineCount;

        //Use Dispatcher to undo - http://stackoverflow.com/a/25453051/685341
        if (textLineCount > TextBoxMaxAllowedLines.Value)
            Dispatcher.BeginInvoke(DispatcherPriority.Input, (Action) (() => textBox.Undo()));
    }
}

This requires System.Windows.InterActivity to be added to the project and referenced in XAML thusly:

xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"

Collected from the Internet

Please contact [email protected] to delete if infringement.

edited at
0

Comments

0 comments
Login to comment

Related

how to change label to red if number entered into textbox

How to Limit number of lines in JTextArea?

How to limit the number of lines in UITextView?

Limit number of input lines in Java?

Set a limit on the amount of numbers that can be entered into a "masked textbox"

Check the entered value in textboxes are double number in WPF

Wpf multiline textbox style that affects the individual lines

How to enable a textbox and wait for criteria to be entered before delete operation in WPF

WPF How to change the entered character when clicking on the textbox?

AngularJS to limit number of CheckBoxes to be checked and TextBox to input

Limit number of digits after comma in textbox

Summing up all entered number in a textbox and display it in a disabled box

show div dynamically when user entered number in a textbox

Short ascending textbox number lines array order

How to limit the number of customers entered Asp.net MVC

How to limit number of lines in contenteditable div

Swift - Limit the number of characters in lines for UITextview

limit TextView to certain number of lines in swift 2

Limit number of lines able to write with textarea

How to limit the number of lines in grid css?

WPF TextBox accepts only one number

Limit number of displayed items in WPF ListView

c# WPF TextBox bug when retrieve lines by index?

How to limit number of specific character of a textbox c# form application

Scroll text to the left inside the TextBox automatically as the entered text reaches the right side of TextBox. C#, WPF, MVVM

How to dynamically change the number of characters to be entered in textbox, based on the length of a database value?

Hi,I need MVC razor view program to find the multiplication table of input number(entered in textbox)

How can I create a catch that ensures nothing other than a number is entered into a textbox but also accepts a blank entry

How do you get the number that is in the center of numbers entered in a textbox vb.net