通过编码表示约束TextBox中Text的长度

时间:2017-08-01 09:05:41

标签: c# wpf xaml textbox maxlength

我在WPF中有一个TextBox。我想限制TextBox中文本的长度。通过属性MaxLength可以轻松限制字符数。

在我的用例中,我需要限制文本而不是字符数,而是限制给定编码中文本的二进制表示的长度。由于德国人使用该程序,因此有一些变音,消耗两个字节。

我已经有了一个方法,检查给定的字符串是否适合给定的长度:

public bool IsInLength(string text, int maxLength, Encoding encoding)
{
    return encoding.GetByteCount(text) < maxLength;
}

是否有人知道如何以某种方式将此功能绑定到文本框,用户无法输入太多字符以超过最大字节长度。

没有EventHandler的解决方案是首选,因为TextBox位于DataTemplate

2 个答案:

答案 0 :(得分:4)

ValidationRule可能适合这里的账单。这是一个示例实现:

public sealed class ByteCountValidationRule : ValidationRule
{
    // For this example I test using an emoji () which will take 2 bytes and fail this rule.
    static readonly int MaxByteCount = 1;

    static readonly ValidationResult ByteCountExceededResult = new ValidationResult(false, $"Byte count exceeds the maximum allowed limit of {MaxByteCount}");

    public override ValidationResult Validate(object value, CultureInfo cultureInfo)
    {
        var val = value as string;

        return val != null && Encoding.UTF8.GetByteCount(val) > MaxByteCount
            ? ByteCountExceededResult
            : ValidationResult.ValidResult;
    }
}

XAML使用:

    <TextBox.Text>
        <Binding Path="Text" UpdateSourceTrigger="PropertyChanged">
            <Binding.ValidationRules>
                <local:ByteCountValidationRule />
            </Binding.ValidationRules>
        </Binding>
    </TextBox.Text>

现在你可以输入1个表情符号或2个ascii字符来触发失败(因为它们将超过1个字节的限制)。

答案 1 :(得分:-1)

我扩展了Alex Klaus的解决方案,以防止输入太长的文本。

public class TextBoxMaxLengthBehavior : Behavior<TextBox>
{
    public static readonly DependencyProperty MaxLengthProperty =
        DependencyProperty.Register(
            nameof(MaxLength),
            typeof(int),
            typeof(TextBoxMaxLengthBehavior),
            new FrameworkPropertyMetadata(0));

    public int MaxLength
    {
        get { return (int) GetValue(MaxLengthProperty); }
        set { SetValue(MaxLengthProperty, value); }
    }

    public static readonly DependencyProperty LengthEncodingProperty =
        DependencyProperty.Register(
            nameof(LengthEncoding),
            typeof(Encoding),
            typeof(TextBoxMaxLengthBehavior),
            new FrameworkPropertyMetadata(Encoding.Default));

    public Encoding LengthEncoding
    {
        get { return (Encoding) GetValue(LengthEncodingProperty); }
        set { SetValue(LengthEncodingProperty, value); }
    }

    protected override void OnAttached()
    {
        base.OnAttached();

        AssociatedObject.PreviewTextInput += PreviewTextInputHandler;
        DataObject.AddPastingHandler(AssociatedObject, PastingHandler);
    }

    protected override void OnDetaching()
    {
        base.OnDetaching();

        AssociatedObject.PreviewTextInput -= PreviewTextInputHandler;
        DataObject.RemovePastingHandler(AssociatedObject, PastingHandler);
    }

    private void PreviewTextInputHandler(object sender, TextCompositionEventArgs e)
    {
        string text;
        if (AssociatedObject.Text.Length < AssociatedObject.CaretIndex)
            text = AssociatedObject.Text;
        else
        {
            //  Remaining text after removing selected text.
            string remainingTextAfterRemoveSelection;

            text = TreatSelectedText(out remainingTextAfterRemoveSelection)
                ? remainingTextAfterRemoveSelection.Insert(AssociatedObject.SelectionStart, e.Text)
                : AssociatedObject.Text.Insert(AssociatedObject.CaretIndex, e.Text);
        }

        e.Handled = !ValidateText(text);
    }

    private bool TreatSelectedText(out string text)
    {
        text = null;
        if (AssociatedObject.SelectionLength <= 0)
            return false;

        var length = AssociatedObject.Text.Length;
        if (AssociatedObject.SelectionStart >= length)
            return true;

        if (AssociatedObject.SelectionStart + AssociatedObject.SelectionLength >= length)
            AssociatedObject.SelectionLength = length - AssociatedObject.SelectionStart;

        text = AssociatedObject.Text.Remove(AssociatedObject.SelectionStart, AssociatedObject.SelectionLength);
        return true;
    }

    private void PastingHandler(object sender, DataObjectPastingEventArgs e)
    {
        if (e.DataObject.GetDataPresent(DataFormats.Text))
        {
            var pastedText = Convert.ToString(e.DataObject.GetData(DataFormats.Text));
            var text = ModifyTextToFit(pastedText);

            if (!ValidateText(text))
                e.CancelCommand();
            else if (text != pastedText)
                e.DataObject.SetData(DataFormats.Text, text);

        }
        else
            e.CancelCommand();
    }

    private string ModifyTextToFit(string text)
    {
        var result = text.Remove(MaxLength);
        while (!string.IsNullOrEmpty(result) && !ValidateText(result))
            result = result.Remove(result.Length - 1);

        return result;
    }

    private bool ValidateText(string text)
    {
        return LengthEncoding.GetByteCount(text) <= MaxLength;
    }
}

在XAML中,我可以像这样使用它:

<DataTemplate DataType="{x:Type vm:StringViewModel}">
    <TextBox Text="{Binding Path=Value, UpdateSourceTrigger=PropertyChanged, ValidatesOnNotifyDataErrors=True}">
        <i:Interaction.Behaviors>
            <b:TextBoxMaxLengthBehavior MaxLength="{Binding MaxLength}" LengthEncoding="{Binding LengthEncoding}" />
        </i:Interaction.Behaviors>
    </TextBox>
</DataTemplate>

其中xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"。我希望这会对其他人有所帮助。