2016-03-17 62 views
0

在WPF項目中,我使用INotifyDataErrorInfo實現驗證了TextBox輸入。可能會發生多個錯誤。INotifyDataErrorInfo多個驗證錯誤,舊消息不會消失

當我輸入導致多個錯誤的內容時,顯示所有驗證錯誤。但是,當我修復一個錯誤時,驗證消息不會更改,這意味着會顯示錯誤的錯誤消息。只有當我修復所有錯誤時,消息纔會消失。

這是我的實現問題,還是WPF實現只重新獲取驗證消息,如果HasErrors更改?但是,通過調試器遍歷它,我可以看到GetErrors和HasErrors都被調用。

步驟來重現與附例如:

  • 輸入333333. 2驗證消息被示出。
  • 將前導3更改爲2.儘管第一個錯誤已修復,但仍會顯示兩條驗證消息。
  • 將第二個3更改爲0.兩個消息都消失
  • 將第二個數字再次更改爲3。顯示第二個驗證消息。
  • 再次將前導數字更改爲3。只顯示第二條驗證消息,儘管它應該同時顯示。

是的,這個例子沒有多大意義,因爲我實際上可以擺脫第一次檢查,因爲它包含在第二次檢查中。

的視圖:

<Window x:Class="WeirdValidationTest.ValidationTestView" 
     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="clr-namespace:WeirdValidationTest" 
     Height="60" Width="400"> 
    <Window.DataContext> 
     <local:ValidationTestViewModel /> 
    </Window.DataContext> 
    <Window.Resources> 
     <local:ValidationErrorsToStringConverter x:Key="ValErrToString" /> 
     <ControlTemplate x:Key="ErrorTemplate"> 
      <Border BorderBrush="Red" BorderThickness="1"> 
       <StackPanel Orientation="Horizontal"> 
        <AdornedElementPlaceholder /> 
        <TextBlock Text="{Binding Converter={StaticResource ValErrToString}}" Background="White" /> 
       </StackPanel> 
      </Border> 
     </ControlTemplate> 
    </Window.Resources> 
    <Grid> 
     <TextBox MaxLength="6" Validation.ErrorTemplate="{StaticResource ErrorTemplate}" 
       Text="{Binding InputValue, UpdateSourceTrigger=PropertyChanged}" HorizontalAlignment="Left" 
       VerticalAlignment="Top" Width="100" /> 
    </Grid> 
</Window> 

(代碼隱藏在構造簡單InitializeComponent調用)

的視圖模型:

using System; 
using System.Collections; 
using System.Collections.Generic; 
using System.ComponentModel; 
using System.Runtime.CompilerServices; 

namespace WeirdValidationTest 
{ 
    internal class ValidationTestViewModel : INotifyPropertyChanged, INotifyDataErrorInfo 
    { 
     private readonly Dictionary<string, List<string>> errors = new Dictionary<string, List<string>>(); 
     private uint inputValue; 

     public event PropertyChangedEventHandler PropertyChanged; 

     public event EventHandler<DataErrorsChangedEventArgs> ErrorsChanged; 

     public uint InputValue 
     { 
      get 
      { 
       return inputValue; 
      } 
      set 
      { 
       if (inputValue != value) 
       { 
        if (value/100000 == 2) 
        { 
         RemoveError("InputValue", 
            String.Format("Must be in range {0}...{1}", "200000", "299999")); 
        } 
        else 
        { 
         AddError("InputValue", 
           String.Format("Must be in range {0}...{1}", "200000", "299999")); 
        } 

        uint testNumber = (uint) ((value)/1e4); 

        { 
         string msg = string.Format("Must start with value {0}", "20...."); 
         if (testNumber != 20) 
         { 
          AddError("InputValue", msg); 
         } 
         else 
         { 
          RemoveError("InputValue", msg); 
         } 
        } 

        inputValue = value; 
        OnPropertyChanged(); 
       } 
      } 
     } 

     public bool HasErrors 
     { 
      get 
      { 
       return errors.Count != 0; 
      } 
     } 

     public IEnumerable GetErrors(string propertyName) 
     { 
      List<string> val; 
      errors.TryGetValue(propertyName, out val); 
      return val; 
     } 

     void AddError(string propertyName, string messageText) 
     { 
      List<string> errList; 
      if (errors.TryGetValue(propertyName, out errList)) 
      { 
       if (!errList.Contains(messageText)) 
       { 
        errList.Add(messageText); 
       } 
      } 
      else 
      { 
       errList = new List<string> { messageText }; 
       errors.Add(propertyName, errList); 
      } 
      OnErrorsChanged(propertyName); 
     } 

     void RemoveError(string propertyName, string messageText) 
     { 
      List<string> errList; 
      if (errors.TryGetValue(propertyName, out errList)) 
      { 
       errList.Remove(messageText); 
       if (errList.Count == 0) 
       { 
        errors.Remove(propertyName); 
       } 
      } 
      OnErrorsChanged(propertyName); 
     } 

     private void OnErrorsChanged(string propertyName) 
     { 
      var handler = ErrorsChanged; 
      if (handler != null) 
      { 
       handler(this, new DataErrorsChangedEventArgs(propertyName)); 
      } 
     } 

     private void OnPropertyChanged([CallerMemberName] string propertyName = "") 
     { 
      var handler = PropertyChanged; 
      if (handler != null) 
      { 
       handler(this, new PropertyChangedEventArgs(propertyName)); 
      } 
     } 
    } 
} 

變換器:

using System; 
using System.Collections.ObjectModel; 
using System.Globalization; 
using System.Linq; 
using System.Windows; 
using System.Windows.Controls; 
using System.Windows.Data; 

namespace WeirdValidationTest 
{ 
    [ValueConversion(typeof(ReadOnlyObservableCollection<ValidationError>), typeof(string))] 
    internal class ValidationErrorsToStringConverter : IValueConverter 
    { 
     public object Convert(object value, Type targetType, object parameter, CultureInfo culture) 
     { 
      var errorCollection = value as ReadOnlyObservableCollection<ValidationError>; 

      if (errorCollection == null) 
      { 
       return DependencyProperty.UnsetValue; 
      } 

      return String.Join(", ", errorCollection.Select(e => e.ErrorContent.ToString())); 
     } 

     public object ConvertBack(object value, Type targetType, object parameter, 
            CultureInfo culture) 
     { 
      throw new NotImplementedException(); 
     } 
    } 
} 

目標。淨版本4.5

編輯: 打一個類似的問題與IDataErrorInfo,看到了這個問題: Validation Rule not updating correctly with 2 validation rules 更改轉換器有助於

回答

1

很好解釋,你的代碼看起來相當不錯。我用你的方式解決了你的問題,希望你喜歡它。 只需更改您的AddError和RemoveError方法就可以,

void AddError(string propertyName, string messageText) 
     { 
      List<string> errList; 
      if (errors.TryGetValue(propertyName, out errList)) 
      { 
       if (!errList.Contains(messageText)) 
       { 
        errList.Add(messageText); 
        errors.Remove(propertyName); 
        OnErrorsChanged(propertyName); 
        if (errList != null) 
         errors.Add(propertyName, errList); 
       } 
      } 
      else 
      { 
       errList = new List<string> { messageText }; 
       errors.Add(propertyName, errList); 
       OnErrorsChanged(propertyName); 
      } 
     } 
     void RemoveError(string propertyName, string messageText) 
     { 
      List<string> errList; 
      if (errors.TryGetValue(propertyName, out errList)) 
      { 
       errList.Remove(messageText); 
       errors.Remove(propertyName); 
      } 
      OnErrorsChanged(propertyName); 
      if (errList != null) 
       errors.Add(propertyName, errList); 

     } 
+0

適合我,謝謝。只有2個問題:爲什麼在這兩種方法中沒有最終的OnErrorsChanged(Works without,但我實際上會猜測它是需要的)?那麼errList!= null檢查方法的末尾是什麼(它應該是在方法的開始,我想我只是在我的例子中忘了它) –