在代码中创建一个控件模板的visualtree

时间:2011-09-03 13:07:34

标签: wpf validation controltemplate

这是对上一个问题的跟进问题,这对我来说并没有真正让我感到满意:  deterministic and asynchronous field validation in WPF

由于WPF似乎不支持INotifyDataErrorInfo,我需要自己实现类似的东西(如果我错了,请纠正我)。我需要这个,因为我希望ViewModel触发何时为某些字段显示特殊的ErrorTemplates(例如,在单击按钮之后或在长时间运行异步验证操作结束之后或当内部状态以某些字段突然显示的方式更改时)失效)。

我正在考虑为此编写自定义标记扩展或行为。它监听我的ViewModel实现的INotifyDataErrorInfo版本,并在引发ErrorsChanged事件后,从XAML中定义的特殊的已知ErrorTemplate创建一个VisualTree。

一旦我在XAML中定义了该模板,我如何从我的行为/表达中找到它,从中实现一个实际的可视化树,然后在我右边的字段条目中显示它(可能在某种方式在一个装饰层上)形成的?

2 个答案:

答案 0 :(得分:6)

您不需要标记扩展名。我最近发现自己希望有同样的行为,所以我创建了一个适合我需求的解决方案。希望这对你也有帮助。

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属性添加到测试视图模型并将其绑定到Button。 (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以及相关的错误消息。

答案 1 :(得分:0)

INotifyDataErrorInfo现已包含在 WPF 4.5 中以及许多其他功能中。请参阅以下链接

以下是Visual Studio 11开发人员预览的链接:
http://msdn.microsoft.com/en-us/vstudio/hh127353