您不需要标记扩展。我最近发现自己希望有同样的行为,所以我创建了一个满足我需求的解决方案。希望这对您也有帮助。
IDataErrorInfo 接口实际上包含了我们执行异步信令所需的一切。它缺少的是一个自动触发通知的事件系统。该接口与INotifyPropertyChanged 接口之间存在关系。两者的结合实际上可以让您在某种程度上间接地发出变化信号。
首先是控件:
<TextBox
Grid.Column="1"
Width="100"
Text="{Binding UpdateSourceTrigger=LostFocus,
Path=Id,
ValidatesOnDataErrors=true}" />
非常简单。 UpdateSourceTrigger 的值不重要,NotifyOnValidationError 也不是必须的,但加了也无妨。
接下来是视图模型,这只是一个人为的示例。重要的部分在IDataErrorInfo 索引器中。
public class WindowViewModel : INotifyPropertyChanged, IDataErrorInfo
{
public event PropertyChangedEventHandler PropertyChanged;
private int _id;
public int Id
{
get{ return _id; }
set
{
_id = value;
this.PropertyChanged( this, new PropertyChangedEventArgs( "Id" ) );
}
}
public string this[ string columnName ]
{
get
{
object result = AsynchValidationCoordinator.GetError( columnName );
if ( result != null )
{
return result.ToString();
}
return null;
}
}
AsynchValidationCoordinator 是一个跟踪属性和任何相关错误信息的类。出于说明目的,使用的键只是属性名称,但您可以轻松创建复合键来区分具有多个视图模型的场景中的潜在属性冲突。
public static class AsynchValidationCoordinator
{
private static readonly ConcurrentDictionary<string, object> ErrorList =
new ConcurrentDictionary<string, object>();
public static void CancelError( string propertyName, object error )
{
object value;
ErrorList.TryRemove( propertyName, out value );
}
public static object GetError( string propertyName )
{
object error = null;
if ( ErrorList.ContainsKey( propertyName ) )
{
ErrorList.TryRemove( propertyName, out error );
}
return error;
}
public static void RegisterError( string propertyName, object error )
{
ErrorList[propertyName] = error;
}
}
跟踪属性名称是必要的,但您可以创建一种完全不同的方法来跟踪它们,包括在视图模型中跟踪名称。这只是我将结构化表单快速应用于现有项目的一种简单方法。
因此,将这一切联系在一起,我将以下ICommand 属性添加到测试视图模型并将其绑定到按钮。 (RelayCommand 来自 Josh Smith 的MSDN MVVM article。)
public ICommand ValidateCommand
{
get
{
return new RelayCommand( Validate );
}
}
private void Validate( object value )
{
Thread thread = new Thread( RaiseChanged );
thread.Start();
}
private void RaiseChanged()
{
Thread.Sleep( 3000 );
AsynchValidationCoordinator.RegisterError( "Id", "Error Message Goes Here" );
this.PropertyChanged( this, new PropertyChangedEventArgs( "Id" ) );
}
调用的来源无关紧要。将所有这些联系在一起的重要一点是,一旦调用了PropertyChanged,IDataErrorInfo 索引器就会跟随它的轨道。返回AsynchValidationCoordinator中注册的错误信息会触发控件的Validation.ErrorTemplate,并带有相关的错误信息。