2017-03-31 15 views
6

Hiện tại tôi có lưới và tôi đang cố gắng để có một ô có quy tắc xác thực. Để xác nhận nó, tôi yêu cầu giá trị tối thiểu và tối đa của hàng.Lưới WPF với quy tắc xác thực và thuộc tính phụ thuộc

Validation Class:

public decimal Max { get; set; } 

public decimal Min { get; set; } 

public override ValidationResult Validate(object value, System.Globalization.CultureInfo cultureInfo) 
{ 
    var test = i < Min; 
    var test2 = i > Max; 

    if (test || test2) 
     return new ValidationResult(false, String.Format("Fee out of range Min: ${0} Max: ${1}", Min, Max)); 
    else 
     return new ValidationResult(true, null); 
} 

Bản quyền thuộc về:

<telerik:RadGridView SelectedItem ="{Binding SelectedScript}" 
        ItemsSource="{Binding ScheduleScripts}"> 
    <telerik:RadGridView.Columns> 
     <telerik:GridViewDataColumn 
      DataMemberBinding="{Binding Amount}" Header="Amount" 
      CellTemplate="{StaticResource AmountDataTemplate}" 
      CellEditTemplate="{StaticResource AmountDataTemplate}"/> 
     <telerik:GridViewComboBoxColumn 
      Header="Fee Type" 
      Style="{StaticResource FeeTypeScriptStyle}" 
      CellTemplate="{StaticResource FeeTypeTemplate}"/>   
    </telerik:RadGridView.Columns> 
</telerik:RadGridView> 

FeeType Class:

public class FeeType 
{ 
    public decimal Min { get; set; } 
    public decimal Max { get; set; } 
    public string Name { get; set; } 
} 

Tôi đã thử giải pháp này đây WPF ValidationRule with dependency property và nó hoạt động tuyệt vời. Nhưng bây giờ tôi đi qua các vấn đề mà các proxy không thể được instantiated thông qua viewmodel. Nó dựa trên thuộc tính Min và Max đã chọn của ComboBox Value.

Ví dụ, rằng giá trị mẫu combo box dưới

Admin Min: $75 Max $500 
Late Min: $0 Max $50 

Kể từ khi một mạng lưới có thể có hầu như nhiều hàng như nó muốn, tôi không thể nhìn thấy cách tạo proxy sẽ làm việc trong hoàn cảnh của tôi. Nếu tôi có thể nhận được một số lời khuyên hướng dẫn, sẽ được đánh giá cao.

+0

Chỉ có một 'ComboBox' trong mã của bạn. – AnjumSKhan

+0

@AnjumSKhan Chỉ giả sử là một ComboBox. Giá trị comboBox thuộc loại FeeType. Vì vậy, bất cứ điều gì được chọn sẽ xác định nó là min và max. – Master

+4

Bạn có chắc chắn rằng đây không phải là vấn đề [XY] (https://meta.stackexchange.com/questions/66377/what-is-the-xy-problem)? Nó sẽ mất rất nhiều công sức để làm điều này bằng cách sử dụng một 'ValidationRule', trong khi nó sẽ được khá dễ dàng để thực hiện nếu bạn di chuyển logic xác nhận để xem-mô hình. – Grx70

Trả lời

4

Cảnh báo: đây không phải là giải pháp dứt khoát, nhưng cho bạn thấy một cách chính xác để triển khai logic xác thực đặt hoàn toàn trên Chế độ xem.

Đối với mục đích semplicity, tôi có thể tạo danh sách các FeeTypes như thuộc tính tĩnh của lớp FeeType:

public class FeeType 
{ 
    public decimal Min { get; set; } 
    public decimal Max { get; set; } 
    public string Name { get; set; } 

    public static readonly FeeType[] List = new[] 
    { 
     new FeeType { Min = 0, Max = 10, Name = "Type1", }, 
     new FeeType { Min = 2, Max = 20, Name = "Type2", }, 
    }; 
} 

Đây là ViewModel cho một hàng lưới duy nhất. Tôi chỉ đặt các thuộc tính Amount và Fee.

public class RowViewModel : INotifyPropertyChanged, INotifyDataErrorInfo 
{ 
    public RowViewModel() 
    { 
     _errorFromProperty = new Dictionary<string, string> 
     { 
      { nameof(Fee), null }, 
      { nameof(Amount), null }, 
     }; 

     PropertyChanged += OnPropertyChanged; 
    } 

    private void OnPropertyChanged(object sender, PropertyChangedEventArgs e) 
    { 
     switch(e.PropertyName) 
     { 
      case nameof(Fee): 
       OnFeePropertyChanged(); 
       break; 
      case nameof(Amount): 
       OnAmountPropertyChanged(); 
       break; 
      default: 
       break; 
     } 
    } 

    private void OnFeePropertyChanged() 
    { 
     if (Fee == null) 
      _errorFromProperty[nameof(Fee)] = "You must select a Fee!"; 
     else 
      _errorFromProperty[nameof(Fee)] = null; 

     NotifyPropertyChanged(nameof(Amount)); 
    } 

    private void OnAmountPropertyChanged() 
    { 
     if (Fee == null) 
      return; 

     if (Amount < Fee.Min || Amount > Fee.Max) 
      _errorFromProperty[nameof(Amount)] = $"Amount must be between {Fee.Min} and {Fee.Max}!"; 
     else 
      _errorFromProperty[nameof(Amount)] = null; 
    } 

    public decimal Amount 
    { 
     get { return _Amount; } 
     set 
     { 
      if (_Amount != value) 
      { 
       _Amount = value; 
       NotifyPropertyChanged(); 
      } 
     } 
    } 
    private decimal _Amount; 

    public FeeType Fee 
    { 
     get { return _Fee; } 
     set 
     { 
      if (_Fee != value) 
      { 
       _Fee = value; 
       NotifyPropertyChanged(); 
      } 
     } 
    } 
    private FeeType _Fee; 

    #region INotifyPropertyChanged 
    public event PropertyChangedEventHandler PropertyChanged; 
    public void NotifyPropertyChanged([CallerMemberName] string propertyName = null) 
    { 
     PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); 
    } 
    #endregion 

    #region INotifyDataErrorInfo 
    public event EventHandler<DataErrorsChangedEventArgs> ErrorsChanged; 

    public bool HasErrors 
    { 
     get 
     { 
      return _errorFromProperty.Values.Any(x => x != null); 
     } 
    } 

    public IEnumerable GetErrors(string propertyName) 
    { 
     if (string.IsNullOrEmpty(propertyName)) 
      return _errorFromProperty.Values; 

     else if (_errorFromProperty.ContainsKey(propertyName)) 
     { 
      if (_errorFromProperty[propertyName] == null) 
       return null; 
      else 
       return new[] { _errorFromProperty[propertyName] }; 
     } 

     else 
      return null; 
    } 

    private Dictionary<string, string> _errorFromProperty; 
    #endregion 
} 

Bây giờ, tôi thử nghiệm nó với một người gốc DataGrid, nhưng kết quả sẽ là như nhau trong Telerik:

<DataGrid AutoGenerateColumns="False" ItemsSource="{Binding Rows}"> 
    <DataGrid.Columns> 
    <DataGridTextColumn Binding="{Binding Amount}"/> 
    <DataGridComboBoxColumn SelectedItemBinding="{Binding Fee, UpdateSourceTrigger=PropertyChanged}" 
          ItemsSource="{x:Static local:FeeType.List}" 
          DisplayMemberPath="Name" 
          Width="200"/> 
    </DataGrid.Columns> 
</DataGrid> 

public partial class MainWindow : Window 
{ 
    public MainWindow() 
    { 
     InitializeComponent(); 

     Rows = new List<RowViewModel> 
     { 
      new RowViewModel(), 
      new RowViewModel(), 
     }; 

     DataContext = this; 
    } 

    public List<RowViewModel> Rows { get; } 
} 

Nếu một trường hợp FeeType có thể sửa đổi MinMax khi chạy, bạn cần phải thực hiện INotifyPropertyChanged cũng thuộc lớp đó, xử lý giá trị thay đổi một cách thích hợp.

Nếu bạn mới sử dụng những thứ "MVVM", "Chế độ xem", "Thay đổi thông báo" v.v., hãy xem this article. Nếu bạn thường làm việc trên dự án trung bình lớn trên WPF, nó là giá trị học tập làm thế nào để tách View và Logic thông qua mô hình MVVM. Điều này cho phép bạn kiểm tra logic theo cách nhanh hơn và tự động hơn, đồng thời giữ cho mọi thứ được tổ chức và tập trung.

+0

Mặc dù nó có thể không được yêu cầu trong ví dụ cụ thể này, nó sẽ là thực hành tốt để cũng tăng sự kiện 'INotifyDataErrorInfo.ErrorsChanged'. – Grx70

+0

@ Grx70, tốt, thực sự WPF không yêu cầu bạn phải nâng cao sự kiện 'ErrorsChanged' trong các tình huống đơn giản: nó kiểm tra lại các lỗi khi một sự kiện' PropertyChanged' được nâng lên. Bạn có thể thấy điều này nếu bạn sao chép mã của tôi trong một giải pháp VS và chạy nó. Việc tăng sự kiện 'ErrorsChanged' chỉ hữu ích trong các kịch bản phức tạp hơn, và vì mã của tôi đã tồn tại từ lâu, tôi không muốn làm cho nó dài hơn chỉ để hoàn thành một vấn đề mà _ trong trường hợp này chỉ là lý thuyết. –

Các vấn đề liên quan