2017-08-01 32 views
5

我在WPF中有TextBox。我想限制TextBox中文字的長度。有一個簡單的方法來限制屬性MaxLength的字符數。通過其編碼表示約束TextBox中的文本的長度

在我的用例中,我需要限制文本不是由字符數,而是通過給定編碼中文本的二進制表示的長度來限制文本。由於該程序被德國人使用,所以有一些變音符號消耗兩個字節。

我已經有一個方法,是檢查,如果給定的字符串適應給定長度:

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

有誰有一個想法如何給這個函數綁在文本框的方式,用戶沒有可能輸入太多字符來超過最大字節長度。

沒有EventHandler的解決方案是首選的,因爲TextBox在DataTemplate之內。

+0

你使用dataannotations和驗證接口(Inotfiy ...)來驗證輸入嗎? – Jehof

+0

是的,我這樣做。那麼有沒有辦法? – scher

+0

綁定到文本框的屬性上的自定義驗證屬性 – Jehof

回答

3

A 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字符來觸發失敗(因爲無論將超過100個字節的限制)。

+0

感謝您的回覆。這是一個很好的解決方案。使用ValidationRule對我來說是新的,並將幫助我進一步應用。不過,我正在尋找一種解決方案,用戶無法輸入太多字符。使用ValidationRule,用戶可以輸入太多字符,但會出錯。 – scher

+0

我強烈懷疑你可以實現使用原生文本框。用戶可以直觀地輸入字符,但視圖模型不會使用無效信息進行更新。這就是系統的工作原理。解決方法是向MaxLength添加一個綁定,這可能會動態地減少可以作爲用戶類型輸入的字符數量,但即使如此,除了令人困惑的用戶體驗外,還無法達到100%的預期結果。我強烈建議不要採取這種改變控制默認預期行爲的方式。 – Maverik

+0

我正在使用['Behavior'](https://msdn.microsoft.com/en-us/library/system.windows.interactivity.behavior(v = expression.40))解決方案。ASPX)。如果可能,我會發布它。 – scher

-2

我已經擴展了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"。我希望這會幫助別人。

+1

爲什麼不使用ValidationRule方法...?它更小,更易於閱讀和工作......並且它與行爲一樣可重用。 –

+0

由於使用ValidationRule,用戶可以輸入比允許的更多的字符到文本框中。他或她認爲這是無效的,但那不是我預期的行爲。我真的希望用戶不要輸入太多字符。 – scher