WPF。使用 MVVM 了解 UserControl 中的错误验证
WPF. Understanding error validation inside a UserControl with MVVM
我正在尝试验证 Window 中另一个 UserControl 正在使用的 UserControl 元素中的表单。
我正在使用 MVVM 模式,并且在最后一个 UserControl 子级的 ViewModel 中实现 INotifyDataErrorInfo。
问题是,当发生错误时,UserControl 中绑定到 属性 的 TextBox 和 UserControl 本身都被指示错误的红色框包围,我只想要突出显示的文本框。
代码如下:
有MainView(或第一个UserControl)的Window:
<Grid>
<pages:MainPage>
<pages:MainPage.DataContext>
<vm:MainViewModel/>
</pages:MainPage.DataContext>
</pages:MainPage>
</Grid>
(它只包含一个 UserControl 作为页面)
"MainPage" 的 UserControl,它包含另一个(也是最后一个)UserControl 作为页面内的页面:
<UserControl.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary>
<DataTemplate DataType="{x:Type vm:SearchViewModel}">
<pages:SearchPage/>
</DataTemplate>
...
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</UserControl.Resources>
...
<ContentControl Content="{Binding CurrentPage}"/>
好吧,现在相信我,"CurrentPage" 有一个从 MainViewModel 属性 获取的 ViewModel 对象,所以假设 "CurrentPage" 是一个 "SearchViewModel" 对象,所以有我们有 SearchPage 用户控件。
现在是最后一个 UserControl,SearchPage:
<TextBox Grid.Column="1" Grid.Row="0" Text="{Binding CaseNumber}"/>
<TextBox Grid.Column="1" Grid.Row="1" Text="{Binding PatientNumber}"/>
<TextBox Grid.Column="1" Grid.Row="2" Text="{Binding PatientName}"/>
<TextBox Grid.Column="1" Grid.Row="3" Text="{Binding PatientFamilyName}"/>
<TextBox Grid.Column="1" Grid.Row="4" Text="{Binding PatientMotherMaidenName}"/>
<TextBox Grid.Column="1" Grid.Row="5" Text="{Binding DoctorName}"/>
为了使 post 尽可能小,我刚刚添加了 UserControl 的 "form" 部分。
现在是最重要的部分,带有 INotifyDataErrorInfo 实现的 SearchViewModel:
public class SearchViewModel : ViewModelBase, INotifyDataErrorInfo, IVMPage
{
private SearchModel searchModel = new SearchModel();
public event EventHandler<DataErrorsChangedEventArgs> ErrorsChanged;
private Dictionary<string, List<string>> errors = new Dictionary<string, List<string>>();
private string patientNumber;
public string PatientNumber
{
get { return patientNumber; }
set
{
int number;
patientNumber = value;
if (int.TryParse(value, out number))
{
searchModel.PatientNumber = number;
ClearErrors("PatientNumber");
}
else
{
AddErrors("PatientNumber", new List<string> { "The value must be a number" });
}
RaisePropertyChanged("PatientNumber");
}
}
private string caseNumber;
public string CaseNumber
{
get { return caseNumber; }
set
{
int number;
caseNumber = value;
if (int.TryParse(value, out number))
{
searchModel.CaseNumber = number;
ClearErrors("CaseNumber");
}
else
{
AddErrors("CaseNumber", new List<string> { "The value must be a number" });
}
RaisePropertyChanged("CaseNumber");
}
}
....
private void ClearErrors(string propertyName)
{
errors.Remove(propertyName);
if (ErrorsChanged != null)
ErrorsChanged(this, new DataErrorsChangedEventArgs(propertyName));
}
private void AddErrors(string propertyName, List<string> newErrors)
{
errors.Remove(propertyName);
errors.Add(propertyName, newErrors);
if(ErrorsChanged != null)
ErrorsChanged(this, new DataErrorsChangedEventArgs(propertyName));
}
public System.Collections.IEnumerable GetErrors(string propertyName)
{
if(string.IsNullOrEmpty(propertyName))
{
return errors.Values;
}
else
{
if(errors.ContainsKey(propertyName))
{
return errors[propertyName];
}
else
{
return null;
}
}
}
public bool HasErrors
{
get { return (errors.Count() > 0); }
}
所以,问题是:
例如,如果我在 "CaseNumber" TextBox 中引入字符,它会被一条指示错误的红线包围,并且所有 SearchPage UserControl 也会被另一条红线包围。我想要的只是用红线标记 TextBox 以指示错误,而不是所有 UserControl。
奇怪的是,如果我在触发 ErrorChanged 事件的 AddError 和 ClearError 方法中注释部分,则 UserControl 不再被红线包围...但我不知道为什么... .
抱歉问了这么长的问题,谢谢。
简要版本
为文本框的背景而不是边框着色。
(可选)详细版本
去过那里,运行进入那个问题。文本框的边框相当难以更改,因为它有很多东西。例如,如果您使用的是 DevExpress,则必须覆盖整个文本框样式才能到达边框,然后在选中框时开始失去自然突出显示等。
因此,我建议为文本框的背景着色以指示错误。它对用户来说更明显,看起来很棒,并且在实践中效果很好。
使用非常浅的红色,此页面有助于找到与页面现有配色方案相协调的颜色:
好的,答案很简单。
问题在于这一行:
<ContentControl Content="{Binding CurrentPage}"/>
因为 WPF 默认情况下将 ValidatesOnNotifyDataErrors 属性 设置为 true,当 "CurrentPage" UserControl 内部发生错误时,在 UserControl 内部产生错误的 TextBox 会用红线表示错误他,正如预期的那样,但 ContentControl 也会检查 "GetErrors" 方法并在所有 "CurrentPage" UserControl 周围绘制另一条红线。
为了避免这种情况并仅在 TextBox 而不是所有 UserControl 中指示错误,只需将其添加到 ContentControl 声明中:
<ContentControl Content="{Binding CurrentPage, ValidatesOnNotifyDataErrors=False}"/>
我正在尝试验证 Window 中另一个 UserControl 正在使用的 UserControl 元素中的表单。 我正在使用 MVVM 模式,并且在最后一个 UserControl 子级的 ViewModel 中实现 INotifyDataErrorInfo。 问题是,当发生错误时,UserControl 中绑定到 属性 的 TextBox 和 UserControl 本身都被指示错误的红色框包围,我只想要突出显示的文本框。
代码如下:
有MainView(或第一个UserControl)的Window:
<Grid>
<pages:MainPage>
<pages:MainPage.DataContext>
<vm:MainViewModel/>
</pages:MainPage.DataContext>
</pages:MainPage>
</Grid>
(它只包含一个 UserControl 作为页面)
"MainPage" 的 UserControl,它包含另一个(也是最后一个)UserControl 作为页面内的页面:
<UserControl.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary>
<DataTemplate DataType="{x:Type vm:SearchViewModel}">
<pages:SearchPage/>
</DataTemplate>
...
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</UserControl.Resources>
...
<ContentControl Content="{Binding CurrentPage}"/>
好吧,现在相信我,"CurrentPage" 有一个从 MainViewModel 属性 获取的 ViewModel 对象,所以假设 "CurrentPage" 是一个 "SearchViewModel" 对象,所以有我们有 SearchPage 用户控件。
现在是最后一个 UserControl,SearchPage:
<TextBox Grid.Column="1" Grid.Row="0" Text="{Binding CaseNumber}"/>
<TextBox Grid.Column="1" Grid.Row="1" Text="{Binding PatientNumber}"/>
<TextBox Grid.Column="1" Grid.Row="2" Text="{Binding PatientName}"/>
<TextBox Grid.Column="1" Grid.Row="3" Text="{Binding PatientFamilyName}"/>
<TextBox Grid.Column="1" Grid.Row="4" Text="{Binding PatientMotherMaidenName}"/>
<TextBox Grid.Column="1" Grid.Row="5" Text="{Binding DoctorName}"/>
为了使 post 尽可能小,我刚刚添加了 UserControl 的 "form" 部分。
现在是最重要的部分,带有 INotifyDataErrorInfo 实现的 SearchViewModel:
public class SearchViewModel : ViewModelBase, INotifyDataErrorInfo, IVMPage
{
private SearchModel searchModel = new SearchModel();
public event EventHandler<DataErrorsChangedEventArgs> ErrorsChanged;
private Dictionary<string, List<string>> errors = new Dictionary<string, List<string>>();
private string patientNumber;
public string PatientNumber
{
get { return patientNumber; }
set
{
int number;
patientNumber = value;
if (int.TryParse(value, out number))
{
searchModel.PatientNumber = number;
ClearErrors("PatientNumber");
}
else
{
AddErrors("PatientNumber", new List<string> { "The value must be a number" });
}
RaisePropertyChanged("PatientNumber");
}
}
private string caseNumber;
public string CaseNumber
{
get { return caseNumber; }
set
{
int number;
caseNumber = value;
if (int.TryParse(value, out number))
{
searchModel.CaseNumber = number;
ClearErrors("CaseNumber");
}
else
{
AddErrors("CaseNumber", new List<string> { "The value must be a number" });
}
RaisePropertyChanged("CaseNumber");
}
}
....
private void ClearErrors(string propertyName)
{
errors.Remove(propertyName);
if (ErrorsChanged != null)
ErrorsChanged(this, new DataErrorsChangedEventArgs(propertyName));
}
private void AddErrors(string propertyName, List<string> newErrors)
{
errors.Remove(propertyName);
errors.Add(propertyName, newErrors);
if(ErrorsChanged != null)
ErrorsChanged(this, new DataErrorsChangedEventArgs(propertyName));
}
public System.Collections.IEnumerable GetErrors(string propertyName)
{
if(string.IsNullOrEmpty(propertyName))
{
return errors.Values;
}
else
{
if(errors.ContainsKey(propertyName))
{
return errors[propertyName];
}
else
{
return null;
}
}
}
public bool HasErrors
{
get { return (errors.Count() > 0); }
}
所以,问题是: 例如,如果我在 "CaseNumber" TextBox 中引入字符,它会被一条指示错误的红线包围,并且所有 SearchPage UserControl 也会被另一条红线包围。我想要的只是用红线标记 TextBox 以指示错误,而不是所有 UserControl。
奇怪的是,如果我在触发 ErrorChanged 事件的 AddError 和 ClearError 方法中注释部分,则 UserControl 不再被红线包围...但我不知道为什么... .
抱歉问了这么长的问题,谢谢。
简要版本
为文本框的背景而不是边框着色。
(可选)详细版本
去过那里,运行进入那个问题。文本框的边框相当难以更改,因为它有很多东西。例如,如果您使用的是 DevExpress,则必须覆盖整个文本框样式才能到达边框,然后在选中框时开始失去自然突出显示等。
因此,我建议为文本框的背景着色以指示错误。它对用户来说更明显,看起来很棒,并且在实践中效果很好。
使用非常浅的红色,此页面有助于找到与页面现有配色方案相协调的颜色:
好的,答案很简单。 问题在于这一行:
<ContentControl Content="{Binding CurrentPage}"/>
因为 WPF 默认情况下将 ValidatesOnNotifyDataErrors 属性 设置为 true,当 "CurrentPage" UserControl 内部发生错误时,在 UserControl 内部产生错误的 TextBox 会用红线表示错误他,正如预期的那样,但 ContentControl 也会检查 "GetErrors" 方法并在所有 "CurrentPage" UserControl 周围绘制另一条红线。
为了避免这种情况并仅在 TextBox 而不是所有 UserControl 中指示错误,只需将其添加到 ContentControl 声明中:
<ContentControl Content="{Binding CurrentPage, ValidatesOnNotifyDataErrors=False}"/>