验证规则使用来自另一个控件的值

时间:2013-09-01 00:15:37

标签: c# wpf validation

我正在尝试做一些我以前认为非常容易的事情:在另一个控件的验证规则中使用一个控件的值。我的应用程序具有用户可以输入的各种参数,这里讨论的特定参数定义了范围的起点和终点,用户通过文本框设置值。

有问题的两个控件是开始和结束文本框,应在验证中检查以下条件:

  1. 起始值必须大于或等于某个任意值
  2. 结束值必须小于或等于某个任意值
  3. 起始值必须小于或等于结束值
  4. 我已经完成的前两个条件。第三个实现起来要困难得多,因为我无法从验证器访问结束文本框的值。即使我可以,有五个不同的范围(每个都有自己的开始和结束文本框)我正在尝试验证,并且必须有一些解决方案比为每个范围创建验证规则更优雅。

    以下是相关的XAML代码:

    <Window x:Class="WpfApplication1.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:validators="clr-namespace:CustomValidators"
        Title="MainWindow" Height="350" Width="525">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition/>
            <RowDefinition/>
        </Grid.RowDefinitions>
    
        <TextBox Name="textboxStart" Grid.Row="0">
            <TextBox.Text>
                <Binding Path="Start" UpdateSourceTrigger="PropertyChanged">
                    <Binding.ValidationRules>
                        <validators:MeasurementRangeRule Min="1513" Max="1583"/>
                    </Binding.ValidationRules>
                </Binding>
            </TextBox.Text>
        </TextBox>
    
        <TextBox Name="textboxEnd" Grid.Row="1">
            <TextBox.Text>
                <Binding Path="End" UpdateSourceTrigger="PropertyChanged">
                    <Binding.ValidationRules>
                        <validators:MeasurementRangeRule Min="1513" Max="1583"/>
                    </Binding.ValidationRules>
                </Binding>
            </TextBox.Text>
        </TextBox>
    </Grid>
    

    以下是相关的C#代码:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Data;
    using System.Windows.Documents;
    using System.Windows.Input;
    using System.Windows.Media;
    using System.Windows.Media.Imaging;
    using System.Windows.Navigation;
    using System.Windows.Shapes;
    using System.Runtime.CompilerServices;
    using System.ComponentModel;
    using System.Globalization;
    
    namespace WpfApplication1 {
        /// <summary>
        /// Interaction logic for MainWindow.xaml
        /// </summary>
        public partial class MainWindow : Window {
            public MainWindow () {
                InitializeComponent();
            }
    
            private decimal _start;
            private decimal _end;
            public event PropertyChangedEventHandler PropertyChanged;
    
            public decimal Start {
                get { return _start; }
                set {
                    _start = value;
                    RaisePropertyChanged();
                }
            }
    
            public decimal End {
                get { return _end; }
                set {
                    _end = value;
                    RaisePropertyChanged();
                }
            }
    
            private void RaisePropertyChanged ([CallerMemberName] string propertyName = "") {
                if (PropertyChanged != null) {
                    PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
                }
            }
        }
    }
    
    namespace CustomValidators {
    
        public class MeasurementRangeRule : ValidationRule {
            private decimal _min;
            private decimal _max;
    
            public decimal Min {
                get { return _min; }
                set { _min = value; }
            }
    
            public decimal Max {
                get { return _max; }
                set { _max = value; }
            }
    
            public override ValidationResult Validate (object value, CultureInfo cultureInfo) {
                decimal measurementParameter = 0;
    
                try {
                    if (((string) value).Length > 0)
                        measurementParameter = Decimal.Parse((String) value);
                } catch (Exception e) {
                    return new ValidationResult(false, "Illegal characters or " + e.Message);
                }
    
                if ((measurementParameter < Min) || (measurementParameter > Max)) {
                    return new ValidationResult(false,
                      "Out of range. Enter a parameter in the range: " + Min + " - " + Max + ".");
                } else {
                    return new ValidationResult(true, null);
                }
            }
        }
    }
    

    链接here的问题似乎是相关的,但我无法理解所提供的答案。

    ...谢谢

1 个答案:

答案 0 :(得分:5)

对于任何可能遇到此问题的人来说,实现IDataErrorInfo以更正常地验证错误,并在某些逻辑分组中完成对其他控件的验证要容易得多。我将相关属性(start,end,min和max)封装在一个类中,将控件绑定到这些属性,然后使用IDataErrorInfo接口进行验证。相关代码如下......

XAML:                                                      

    <TextBox Name="textboxStart" Grid.Row="0" Text="{Binding Path=Start, ValidatesOnDataErrors=True}" Margin="5"/>
    <TextBox Name="textboxEnd" Grid.Row="1" Text="{Binding Path=End, ValidatesOnDataErrors=True}" Margin="5"/>
</Grid>

C#:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Runtime.CompilerServices;
using System.ComponentModel;

namespace WpfApplication1 {
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window {
        public MainWindow () {
            InitializeComponent();

            Parameter testParameter = new Parameter(0, 10);
            testGrid.DataContext = testParameter;
        }
    }

    public class Parameter: INotifyPropertyChanged, IDataErrorInfo {
        private decimal _start, _end, _min, _max;
        public event PropertyChangedEventHandler PropertyChanged;

        public Parameter () { }

        public Parameter (decimal min, decimal max) {
            this.Min = min;
            this.Max = max;
        }

        public decimal Start {
            get { return _start; }
            set {
                _start = value;
                //RaisePropertyChanged for both Start and End, because one may need to be marked as invalid because of the other's current setting.
                //e.g. Start > End, in which case both properties are now invalid according to the established conditions, but only the most recently changed property will be validated
                RaisePropertyChanged();
                RaisePropertyChanged("End");
            }
        }

        public decimal End {
            get { return _end; }
            set {
                _end = value;
                //RaisePropertyChanged for both Start and End, because one may need to be marked as invalid because of the other's current setting.
                //e.g. Start > End, in which case both properties are now invalid according to the established conditions, but only the most recently changed property will be validated
                RaisePropertyChanged();
                RaisePropertyChanged("Start");
            }
        }

        public decimal Min {
            get { return _min; }
            set { _min = value; }
        }

        public decimal Max {
            get { return _max; }
            set { _max = value; }
        }

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

        public string Error {
            get { return string.Empty; }
        }

        public string this[string columnName] {
            get {
                string result = string.Empty;

                switch (columnName) {
                    case "Start":
                        if (Start < Min || Start > Max || Start > End) {
                            result = "Out of range. Enter a value in the range: " + Min + " - " + End + ".";
                        }
                        break;
                    case "End":
                        if (End < Min || End > Max || End < Start) {
                            result = "Out of range. Enter a value in the range: " + Start + " - " + Max + ".";
                        }
                        break;
                };

                return result;
            }
        }
    }
}