[英]WPF MVVM Validation DataGrid and disable CommandButton
我創建了一個示例MVVM應用程序。
我想要實現的是:
我想驗證單個單元格/行(這已經在起作用)
我想進行“表格”驗證。 例如,進行跨行驗證,以驗證datagrid的第一列中是否沒有重復的字符串。
你會怎么做? 我不知道如何在視圖模型中實現表單驗證。
我的第一個想法是,每次發生任何更改時,都從背后的代碼中調用視圖模型上的驗證方法。 但是這樣做,我仍然不知道如何將視圖驗證規則中的驗證錯誤通知給ViewModel(例如,如果有人在ID列中輸入文本)。 視圖模型將根本不知道並最終成功驗證,只是因為錯誤的值永遠不會到達它。 好的,我可以使用字符串並在viewmodel中進行整個轉換-但是我不喜歡這個想法,因為我想在WPF中使用轉換器/驗證器的全部功能。
有人做過類似的事情嗎?
https://www.dropbox.com/s/f3a1naewltbl9yp/DataGridValidationTest.zip?dl=0
我們實際上需要處理3種類型的錯誤。
一對一的解決方案
當我們在需要Int的地方輸入String時,WPF的綁定引擎生成的錯誤。
<TextBox VerticalAlignment="Stretch" VerticalContentAlignment="Center" Loaded="TextBox_Loaded"> <TextBox.Text> <Binding Path="ID" UpdateSourceExceptionFilter="ReturnExceptionHandler" UpdateSourceTrigger="PropertyChanged" ValidatesOnDataErrors="True" ValidatesOnExceptions="True" > <Binding.ValidationRules> <CustomValidRule ValidationStep="ConvertedProposedValue"></CustomValidRule> </Binding.ValidationRules> </Binding> </TextBox.Text> </TextBox>
MainWindow.xaml.cs
object ReturnExceptionHandler(object bindingExpression, Exception exception)
{
vm.CanHello = false;
return "This is from the UpdateSourceExceptionFilterCallBack.";
}
為了使Button正確響應,我們需要將4件事粘合在一起: ViewModel,Button,ValidationRules和DataGrid的模板列的文本框。 否則,將無法正確設置ViewModel.CanHello屬性,從而使RelayCommand無效。 現在,ValidationRules:CustomValidRule和NegValidRule尚未粘貼到ViewModel。 為了使他們將驗證結果通知給ViewModel,他們需要觸發一些事件。 我們將使用WPF通過InotifyPropertyChanged遵循的通知模式。 我們將為UI級別驗證規則創建一個接口IViewModelUIRule,以與ViewModel進行交互。
ViewModelUIRuleEvent.cs
using System;
namespace BusinessLogic
{
public interface IViewModelUIRule
{
event ViewModelValidationHandler ValidationDone;
}
public delegate void ViewModelValidationHandler(object sender, ViewModelUIValidationEventArgs e);
public class ViewModelUIValidationEventArgs : EventArgs
{
public bool IsValid { get; set; }
public ViewModelUIValidationEventArgs(bool valid) { IsValid = valid; }
}
}
我們的驗證規則現在將實現此接口。
public class CustomValidRule : ValidationRule, IViewModelUIRule
{
bool _isValid = true;
public bool IsValid { get { return _isValid; } set { _isValid = value; } }
public override ValidationResult Validate(object value, System.Globalization.CultureInfo cultureInfo)
{
int? a = value as int?;
ValidationResult result = null;
if (a.HasValue)
{
if (a.Value > 0 && a.Value < 10)
{
_isValid = true;
result = new ValidationResult(true, "");
}
else
{
_isValid = false;
result = new ValidationResult(false, "must be > 0 and < 10 ");
}
}
OnValidationDone();
return result;
}
private void OnValidationDone()
{
if (ValidationDone != null)
ValidationDone(this, new ViewModelUIValidationEventArgs(_isValid));
}
public event ViewModelValidationHandler ValidationDone;
}
////////////////////////////
public class NegValidRule : ValidationRule, IViewModelUIRule
{
bool _isValid = true;
public bool IsValid { get { return _isValid; } set { _isValid = value; } }
ValidationResult result = null;
public override ValidationResult Validate(object value, System.Globalization.CultureInfo cultureInfo)
{
int? a = value as int?;
if (a.HasValue)
{
if (a.Value < 0)
{
_isValid = true;
result = new ValidationResult(true, "");
}
else
{
_isValid = false;
result = new ValidationResult(false, "must be negative ");
}
}
OnValidationDone();
return result;
}
private void OnValidationDone()
{
if (ValidationDone != null)
ValidationDone(this, new ViewModelUIValidationEventArgs(_isValid));
}
public event ViewModelValidationHandler ValidationDone;
}
現在,我們需要更新ViewModel類以維護驗證規則集合。 並處理我們的自定義驗證規則觸發的ValidationDone事件。
namespace BusinessLogic
{
public class ViewModel : INotifyPropertyChanged
{
private ObservableCollection<ValidationRule> _rules;
public ObservableCollection<ValidationRule> Rules { get { return _rules; } }
public ViewModel()
{
_rules = new ObservableCollection<ValidationRule>();
Rules.CollectionChanged += Rules_CollectionChanged;
MyCollection.CollectionChanged += MyCollection_CollectionChanged;
MyCollection.Add(new Class1("Eins", 1));
MyCollection.Add(new Class1("Zwei", 2));
MyCollection.Add(new Class1("Drei", 3));
}
void Rules_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
foreach (var v in e.NewItems)
((IViewModelUIRule)v).ValidationDone += ViewModel_ValidationDone;
}
void ViewModel_ValidationDone(object sender, ViewModelUIValidationEventArgs e)
{
canHello = e.IsValid;
}
void MyCollection_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
foreach (var v in e.NewItems)
((Class1)v).PropertyChanged += ViewModel_PropertyChanged;
}
void ViewModel_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
// if all validations runs good here
// canHello = true;
}
……
現在,我們已經添加了Rules集合,我們需要向其中添加驗證規則。 為此,我們需要參考我們的驗證規則。 現在,我們使用XAML添加這些規則,因此我們將對綁定到ID字段的TextBox使用TexBox的Loaded事件,以便像這樣訪問它們,
<TextBox VerticalAlignment="Stretch" VerticalContentAlignment="Center" Loaded="TextBox_Loaded">
<TextBox.Text>
<Binding Path="ID" UpdateSourceExceptionFilter="ReturnExceptionHandler" UpdateSourceTrigger="PropertyChanged" ValidatesOnDataErrors="True" ValidatesOnExceptions="True" >
<Binding.ValidationRules>
<b:CustomValidRule ValidationStep="ConvertedProposedValue"></b:CustomValidRule>
</Binding.ValidationRules>
</Binding>
</TextBox.Text>
</TextBox>
//////////////////////
private void TextBox_Loaded(object sender, RoutedEventArgs e)
{
Collection<ValidationRule> rules= ((TextBox)sender).GetBindingExpression(TextBox.TextProperty).ParentBinding.ValidationRules;
foreach (ValidationRule rule in rules)
vm.Rules.Add(rule);
}
自定義后端級別驗證。 這是通過處理Class1對象的PropertyChanged事件來完成的。 請參閱上面列出的ViewModel.cs。
void ViewModel_PropertyChanged(object sender, PropertyChangedEventArgs e) { // if all back-end last level validations run good here // canHello = true; }
注意:我們可以使用反射來避免處理TextBox Loaded事件。 因此,僅將驗證規則添加到即可完成工作。
我不相信有可能使用DataGrid
多個列來驗證行。 但是,正如您提到的,您可以使用viewmodel做到這一點。
您必須將DataGrid
的行存儲在ViewModel
(但我希望您已經在這樣做了)。 您需要實現INotifyDataErrorInfo
。 如果某些錯誤發生更改,則可以使用此界面通知視圖。
然后,每次更改name
屬性時,請驗證是否存在重復項。
您的保存按鈕應使用ICommand
調用保存操作。 在CanExecute
方法中,您可以檢查實現INotifyDataErrorInfo
的對象的HasErrors
屬性,並返回適當的boolean
。 這將相應地禁用按鈕。
金達蠻力進取。 我在我的項目中做了以下設計。 文字很難解釋,希望您能理解我在這里輸入的內容
我將有以下設計
CellLevelViewModel
通過上述設計設置,您所需要做的就是在ViewModelBase上擁有一個EventHandler,該事件處理程序將在ViewModel執行驗證后觸發。 使用此事件觸發父ViewModel來執行其自己的驗證級別,並將錯誤結果填充回根視圖模型。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.