限制在 WPF 文本框中输入的行数

Jay*_*Jay 4 c# wpf textbox limit

我试图限制用户可以在文本框中输入的行数。

我一直在研究 - 我能找到的最接近的是: Limit the max number of chars per line in a textbox

限制文本框中每行的最大字符数,结果是用于 winform。

这不是我所追求的......还值得一提的是,有一个误导性的maxlines属性,我发现它只限制了文本框中显示的内容。

我的要求:

  • 不需要使用等宽字体
  • 将文本框限制为最多 5 行
  • 接受回车
  • 不允许额外的回车
  • 达到最大长度时停止文本输入
  • 包装文本(不要特别关心它是否在单词之间这样做或分解整个单词)
  • 处理粘贴到控件中的文本,并且只会粘贴适合的文本。
  • 没有滚动条
  • 另外 - 这会很好 - 可以选择限制每行的字符数

这些要求用于创建 WYSIWYG 文本框,该文本框将用于捕获最终将被打印的数据,并且字体需要可变 - 如果文本被截断或对于固定大小的行来说太大 - 那么它就会出来以这种方式打印(即使它看起来不正确)。

我已经尝试通过处理事件来自己做这件事——但是我很难做到这一点。到目前为止,这是我的代码。

XAML

 <TextBox TextWrapping="Wrap" AcceptsReturn="True"
        PreviewTextInput="UIElement_OnPreviewTextInput"
        TextChanged="TextBoxBase_OnTextChanged" />
Run Code Online (Sandbox Code Playgroud)

背后的代码

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

        }
    }
Run Code Online (Sandbox Code Playgroud)

这不太正常 - 我在最后一行出现奇怪的行为,并且文本框中的选定位置不断跳动。

顺便说一句,也许我走错了路......我还想知道这是否可以通过使用这样的正则表达式来实现:https : //stackoverflow.com/a/1103822/685341

我对任何想法持开放态度,因为我已经为此苦苦挣扎了一段时间。上面列出的要求是不可变的 - 我无法更改它们。

Jay*_*Jay 5

这是我的最终解决方案 - 我仍然想听听是否有人能想出更好的方法来做到这一点......

这只是处理最大行数——我还没有对最大字符数做任何事情——但它在逻辑上是对我已经做过的事情的简单扩展。

因为我正在处理文本框的 textChanged 事件 - 这也包括传递到控件中 - 我还没有找到一种干净的方法来截断此事件中的文本(除非我单独处理 key_preview) - 所以我只是没有通过撤消允许无效输入。

XAML

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

        </i:Interaction.Behaviors>
    </TextBox>
Run Code Online (Sandbox Code Playgroud)

代码(行为)

/// <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()));
    }
}
Run Code Online (Sandbox Code Playgroud)

这需要将System.Windows.InterActivity添加到项目中并因此在 XAML 中引用:

xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
Run Code Online (Sandbox Code Playgroud)