C# 如何将验证错误传递给可重用UserControl中的子控件
我创建了自己的C# 如何将验证错误传递给可重用UserControl中的子控件,c#,wpf,validation,data-binding,wpf-controls,C#,Wpf,Validation,Data Binding,Wpf Controls,我创建了自己的UserControl,名为PersonNameControl,打算重用。 该控件有三个TextBox字段,并且在其类文件中有三个依赖属性 名字 插入 姓氏 每个依赖项属性值都绑定到一个字段,因此依赖项属性Firstname绑定到FirstnameTextBox,依此类推 我没有明确地设置UserControl的DataContext。 控件应尽可能松散。它应该只通过依赖属性获取它的值(字段)。它甚至不应该寻找类似DataContext的东西 名字: 插入: 姓氏: 和控制
UserControl
,名为PersonNameControl
,打算重用。
该控件有三个TextBox
字段,并且在其类文件中有三个依赖属性
- 名字
- 插入
- 姓氏
TextBox
,依此类推
我没有明确地设置UserControl的DataContext。
控件应尽可能松散。它应该只通过依赖属性获取它的值(字段)。它甚至不应该寻找类似DataContext的东西
名字:
插入:
姓氏:
和控制类:
公共部分类PersonNameControl:UserControl
{
公共PersonNameControl()
{
初始化组件();
}
公共字符串名
{
获取{return(string)GetValue(FirstnameProperty);}
set{SetValue(FirstnameProperty,value);}
}
公共静态只读DependencyProperty FirstnameProperty=
DependencyProperty.Register(“Firstname”、typeof(string)、typeof(PersonNameControl),
新的FrameworkPropertyMetadata(“,FrameworkPropertyMetadataOptions.bindstwoway默认值));
公共字符串插入
{
获取{return(string)GetValue(InsertionProperty);}
set{SetValue(InsertionProperty,value);}
}
公共静态只读DependencyProperty InsertionProperty=
DependencyProperty.Register(“插入”)、typeof(字符串)、typeof(PersonNameControl),
新的FrameworkPropertyMetadata(“,FrameworkPropertyMetadataOptions.bindstwoway默认值));
公共字符串姓氏
{
获取{return(string)GetValue(LastnameProperty);}
set{SetValue(LastnameProperty,value);}
}
公共静态只读DependencyProperty LastnameProperty=
DependencyProperty.Register(“姓氏”、typeof(字符串)、typeof(PersonNameControl),
新的FrameworkPropertyMetadata(“,FrameworkPropertyMetadataOptions.bindstwoway默认值));
}
该控件应在另一个视图中使用,如下所示:
当ViewModel(实现INotifyDataErrorInfo)创建验证错误时,myPersonNameControl
UserControl不会发生任何事情。
我设法创建了一个独立的控件,因为它不依赖于特定的DataContext,不在其codebehind文件中设置自己的DataContext,只通过依赖属性获取其值。值通过绑定交换并显示,但验证错误不会显示。
我想要的是将验证错误传递给UserControl
互联网上的一些解决方案利用了validationadrnersite
,我试过了。但这只适用于一个文本框
如果不让我的控制依赖于外部世界或引入丑陋的额外属性来解决它,我看不到任何解决方案。我认为错误就像一条信息一样通过所有绑定“隧道”到值到达的最后一级。但这似乎不是正确的考虑
编辑:
我添加了ViewModel类
公共类CustomPerformViewModel:ViewModelBase,INotifyDataErrorInfo
{
受保护的字符串\u clientNumber;
受保护日期时间\日期;
受保护字符串_firstname;
受保护的字符串\u插入;
受保护字符串\u lastname;
受保护地址\u地址;
受保护的可观察收集电子邮件;
受保护的可观察收集电话号码;
受保护字符串\u注释;
受保护的bool\u hasserrors;
受保护的IList\u验证错误;
公共IList验证错误
{
获取{return\u validationErrors;}
设置{u validationErrors=value;OnPropertyChanged(“validationErrors”);}
}
公共字符串ClientNumber
{
获取{return\u clientNumber;}
设置{u clientNumber=value;OnPropertyChanged(“clientNumber”);}
}
公共日期时间日期
{
获取{return\u date;}
设置{u date=value;OnPropertyChanged(“日期”);}
}
公共字符串名
{
获取{return\u firstname;}
设置{u firstname=value;OnPropertyChanged(“firstname”);}
}
公共字符串插入
{
获取{return\u insertion;}
设置{u insertion=value;OnPropertyChanged(“insertion”);}
}
公共字符串姓氏
{
获取{return\u lastname;}
设置{u lastname=value;OnPropertyChanged(“lastname”);}
}
公共广播
{
获取{return\u address;}
设置{u address=value;OnPropertyChanged(“地址”);}
}
公开收集电子邮件
{
获取{返回_电子邮件;}
设置{u emails=value;OnPropertyChanged(“emails”);}
}
公共可观测收集电话号码
{
获取{return\u phoneNumbers;}
设置{u phoneNumbers=value;OnPropertyChanged(“phoneNumbers”);}
}
公共字符串注释
{
获取{return\u note;}
设置{u note=value;OnPropertyChanged(“note”);}
}
私有DelegateCommand_saveCustomerCommand;
公共DelegateCommand SaveCustomerCommand
{
获取{return\u saveCustomerCommand;}
私有集{u saveCustomerCommand=value;OnPropertyChanged(“saveCustomerCommand”);}
}
公共自定义视图模型()
{
ValidationErrors=新列表();
SaveCustomerCommand=newdelegateCommand(SaveCustomer,CanSaveCustomer);
}
受保护的void ValidateInput()
{
ValidationErrors.Clear();
客户有效
<Window
x:Class="ValidationSubUI.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:ValidationSubUI"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
Name="MyWindow"
Title="MainWindow"
Width="800"
Height="450"
mc:Ignorable="d">
<Window.DataContext>
<local:MainViewModel />
</Window.DataContext>
<Grid>
<local:SubUserControl
FirstName="{Binding FirstName, Mode=TwoWay}"
LastName="{Binding LastName, Mode=TwoWay}"
ValidationSource="{Binding ElementName=MyWindow, Path=DataContext}" />
</Grid>
</Window>
using GalaSoft.MvvmLight;
using System.ComponentModel;
namespace ValidationSubUI
{
public class MainViewModel : ViewModelBase, IDataErrorInfo
{
public string Error
{
get
{
return string.Empty;
}
}
private string m_FirstName;
public string FirstName
{
get { return m_FirstName; }
set
{
m_FirstName = value;
RaisePropertyChanged();
}
}
private string m_LastName;
public string LastName
{
get { return m_LastName; }
set
{
m_LastName = value;
RaisePropertyChanged();
}
}
public string this[string columnName]
{
get
{
if (columnName == nameof(FirstName))
{
return GetFirstNameError();
}
else if (columnName == nameof(LastName))
{
return GetLastNameError();
}
return null;
}
}
private string GetFirstNameError()
{
string result = string.Empty;
if (string.IsNullOrEmpty(FirstName))
{
result = "First name required";
}
return result;
}
private string GetLastNameError()
{
string result = string.Empty;
if (string.IsNullOrEmpty(LastName))
{
result = "Last name required";
}
return result;
}
}
}
using System.ComponentModel;
using System.Windows;
using System.Windows.Controls;
namespace ValidationSubUI
{
/// <summary>
/// Interaction logic for SubUserControl.xaml
/// </summary>
public partial class SubUserControl : UserControl, IDataErrorInfo
{
public SubUserControl()
{
InitializeComponent();
}
public IDataErrorInfo ValidationSource
{
get { return (IDataErrorInfo)GetValue(ValidationSourceProperty); }
set { SetValue(ValidationSourceProperty, value); }
}
// Using a DependencyProperty as the backing store for ValidationSource. This enables animation, styling, binding, etc...
public static readonly DependencyProperty ValidationSourceProperty =
DependencyProperty.Register("ValidationSource", typeof(IDataErrorInfo), typeof(SubUserControl), new PropertyMetadata(null));
public string FirstName
{
get { return (string)GetValue(FirstNameProperty); }
set { SetValue(FirstNameProperty, value); }
}
// Using a DependencyProperty as the backing store for FirstName. This enables animation, styling, binding, etc...
public static readonly DependencyProperty FirstNameProperty =
DependencyProperty.Register("FirstName", typeof(string), typeof(SubUserControl), new PropertyMetadata(string.Empty));
public string LastName
{
get { return (string)GetValue(LastNameProperty); }
set { SetValue(LastNameProperty, value); }
}
// Using a DependencyProperty as the backing store for LastName. This enables animation, styling, binding, etc...
public static readonly DependencyProperty LastNameProperty =
DependencyProperty.Register("LastName", typeof(string), typeof(SubUserControl), new PropertyMetadata(string.Empty));
public string Error
{
get
{
return string.Empty;
}
}
public string this[string columnName]
{
get
{
if (ValidationSource != null)
{
return ValidationSource[columnName];
}
return null;
}
}
}
}
<UserControl
x:Class="ValidationSubUI.SubUserControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
x:Name="CustomControl"
d:DesignHeight="450"
d:DesignWidth="800"
mc:Ignorable="d">
<UserControl.Resources>
<Style TargetType="{x:Type TextBox}">
<Setter Property="Validation.ErrorTemplate">
<Setter.Value>
<ControlTemplate>
<DockPanel>
<Border BorderBrush="Red" BorderThickness="1">
<AdornedElementPlaceholder x:Name="controlWithError" />
</Border>
<TextBlock
Margin="5,0,0,0"
HorizontalAlignment="Center"
VerticalAlignment="Center"
FontSize="12"
FontWeight="DemiBold"
Foreground="Red"
Text="{Binding ElementName=controlWithError, Path=AdornedElement.ToolTip, Mode=OneWay}" />
</DockPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="Validation.HasError" Value="True">
<Setter Property="ToolTip" Value="{Binding RelativeSource={RelativeSource Self}, Path=(Validation.Errors)[0].ErrorContent}" />
</Trigger>
</Style.Triggers>
</Style>
</UserControl.Resources>
<Grid DataContext="{x:Reference Name=CustomControl}">
<StackPanel>
<TextBox
Width="120"
Height="30"
Margin="5"
HorizontalAlignment="Left"
VerticalAlignment="Top"
Text="{Binding FirstName, Mode=TwoWay, ValidatesOnDataErrors=True}"
TextWrapping="Wrap" />
<TextBox
Width="120"
Height="30"
Margin="5"
HorizontalAlignment="Left"
VerticalAlignment="Top"
Text="{Binding LastName, Mode=TwoWay, ValidatesOnDataErrors=True}"
TextWrapping="Wrap" />
</StackPanel>
</Grid>
</UserControl>