WPF Data Binding exception handling

后端 未结 3 1285
眼角桃花
眼角桃花 2021-01-06 17:17

I have a textbox that is bound to an Integer property. when the user enters something in the textbox that can\'t be converted to an integer (eg. name) an exception will be t

3条回答
  •  耶瑟儿~
    2021-01-06 17:49

    I faced the same issue recently and I used behaviors to solve it (but you don't need them if you don't want, it was just for reusing some code I needed among different views). The main idea is to define some methods in the ViewModel that allow the view to notify errors in the input that the ViewModel cannot detect.

    So, first define those methods in your ViewModel. For simplicity, I will only keep track of the number of errors, but you can store more info about them (like the actual error):

    private int _errorCount = 0;
    void AddUIValidationError()
    {
       _errorCount++;
    }
    
    void RemoveUIValidationError()
    {
       _errorCount--;
    }
    

    Then, in your View, you register for System.Windows.Controls.Validation.ErrorEvent, which is a routed event that lets you know when a component (previously configured to notify data errors) detects errors (like an exception validation error):

    public partial class MyView : UserControl // or whatever it is
    {
        public MyView(MyViewModel viewModel)
        {
            // Possibly ensure that viewModel is not null
            InitializeComponent();
            _myViewModel = viewModel;
    
            this.AddHandler(System.Windows.Controls.Validation.ErrorEvent, new RoutedEventHandler(OnValidationRaised));
        }
    
        private MyViewModel _myViewModel;
    
        private void OnValidationRaised(object sender, RoutedEventArgs e)
        {
            var args = (System.Windows.Controls.ValidationErrorEventArgs)e;
    
            if (_myViewModel != null)
            {
    
                // Check if the error was caused by an exception
                if (args.Error.RuleInError is ExceptionValidationRule)
                {
                    // Add or remove the error from the ViewModel
                    if (args.Action == ValidationErrorEventAction.Added)
                        _myViewModel.AddUIValidationError();
                    else if (args.Action == ValidationErrorEventAction.Removed)
                        _myViewModel.RemoveUIValidationError();
                }
            }
        }
    }
    

    In the CanExecute method of your Command, you would check if the _errorCount field of your ViewModel is more than 0, and in that case, the command should be disabled.

    Please note that you should must add ValidatesOnExceptions=True, NotifyOnValidationError=True to your bindings so this can work. Ex:

    
    

    EDIT:

    Another approach, apart from what Riley mentioned (which is also good, but requires that you map each integer property from your model to a new string property in your ViewModel) is using ValidationRules. You can add ValidationRules that are checked before parsing and calling the property setter. So you could, for example, inherit from ValidationRule and implement the Validate method to ensure that the string can be parsed to an integer. Example:

    public class IntegerValidationRule : ValidationRule
    {
        public override ValidationResult Validate(object value, CultureInfo cultureInfo)
        {
            int number;
            if(Int32.TryParse((string)value, out number) == false)
                return new ValidationResult(false, "It is not a valid number");
            return new ValidationResult(true, null);
        }
    }
    

    And then, in your view define the namespace where IntegerValidationRule is defined:

    
    

    And use the rule in your bindings:

    
        
           
               
    
                  
               
           
        
    
    

    But anyway, you'll need to create classes for each non-string type you want to validate, and I think the Binding syntax now looks a bit long.

    Greetings

提交回复
热议问题