【问题标题】:Create a visualtree off of a control template in code在代码中从控件模板创建可视化树
【发布时间】:2011-09-03 13:07:33
【问题描述】:

这是对上一个问题的后续问题,但并没有真正让我明白: deterministic and asynchronous field validation in WPF

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

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

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

【问题讨论】:

  • 你试过bindinggroups吗?因为它们可用于执行表单验证。 BindingGroups 允许触发验证,至少我可以从文档中看到,我自己从未使用过它们。另一个链接BindingGroups validation
  • @dowhilefor 如果我理解正确,我需要在我的 ViewModel 中引用 BindingGroup 以从我的 ViewModel 显式触发验证。
  • 或者您可以创建一个 ICommand,将其称为 ValidateCommand,将其作为 DP 放入您的控件/表单并将其绑定到您的 ViewModel。此命令的实现在您的控件/表单中。在视图模型中,您现在只需在此绑定命令上调用 Execute 即可触发验证。这样您的 ViewModel 就不需要知道 BindingGroup。就像我说的,以前从未使用过它们,但我想这就是我会这样做的方式。我喜欢尽可能地让 WPF 的东西远离我的 VM。
  • 如何在该命令中触发验证,以便 WPF 显示 ErrorTemplate(或在验证时隐藏它)?

标签: wpf validation controltemplate


【解决方案1】:

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

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" ) );
}

调用的来源无关紧要。将所有这些联系在一起的重要一点是,一旦调用了PropertyChangedIDataErrorInfo 索引器就会跟随它的轨道。返回AsynchValidationCoordinator中注册的错误信息会触发控件的Validation.ErrorTemplate,并带有相关的错误信息。

【讨论】:

  • +1 表示将IDataErrorInfoINotifyPropertyChanged 联系在一起。没想到。如果我理解正确,您只需在希望显示或删除验证错误时触发 additional PropertyChanged 事件以强制 WPF 重新评估 public string this[string columnName] 中的验证。对吗?
  • 感觉有点骇人听闻,但它可能仍然比滚动我自己的 MarkupExtension 和 ErrorTemplate 实现要好。我想 WPF vNext 会带来来自 Sliverlight 的INotifyDataErrorInfoconnect.microsoft.com/VisualStudio/feedback/details/568212/…
  • 外部协调器类可能有助于使它有这种感觉。至少它对我有用。实际上,我在自己的设计中修改了基本视图模型类以跟踪视图模型中的错误,而不是依赖外部集合。它让我更接近INotifyDataErrorInfo 实现,并且通过一点努力,我可能会推出一个几乎完全匹配语法的模型,尽管在 vNext 之前,它仍然需要IDataErrorInfo
【解决方案2】:

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

这里是 Visual Studio 11 开发者预览版的链接:
http://msdn.microsoft.com/en-us/vstudio/hh127353

【讨论】:

  • 是的,一旦 WPF 4.5 可用(2012 年?),这是一个选项。不幸的是,我不能等那么久。
  • @bitbonk:我只是想指出,它可以在开发者预览版中使用,对您和遇到此问题的任何其他人都是可用的。而当它正式发布时,它不仅是一个选项,而是应该使用什么,因为它将被完全实施。
猜你喜欢
  • 2013-10-31
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-03-08
  • 2011-02-23
  • 2011-12-28
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多