使用INotifyDataErrorInfo进行验证时,重点关注控件

时间:2011-12-09 04:21:08

标签: silverlight validation silverlight-4.0 inotifydataerrorinfo

我在Silverlight中使用INotifyDataErrorInfo实现使用简单验证。

提交时我正在验证所有属性以显示所有错误。

当验证发生时,我需要将焦点重新放回第一个带有验证错误的控件。

我们有办法做到这一点吗?有什么建议吗?

1 个答案:

答案 0 :(得分:0)

迟到总比没有好:)

我已经实现了这种行为。

首先,您需要订阅ViewModel ErrorsChangedPropertyChanged方法。我在我的构造函数中执行此操作:

    /// <summary>
    /// Initializes new instance of the View class.
    /// </summary>
    public View(ViewModel viewModel)
    {
        if (viewModel == null)
            throw new ArgumentNullException("viewModel");

        // Initialize the control
        InitializeComponent();  // exception

        // Set view model to data context.
        DataContext = viewModel;

        viewModel.PropertyChanged += new PropertyChangedEventHandler(_ViewModelPropertyChanged);
        viewModel.ErrorsChanged += new EventHandler<DataErrorsChangedEventArgs>(_ViewModelErrorsChanged);
    }

然后为此事件编写处理程序:

    /// <summary>
    /// If model errors has changed and model still have errors set flag to true, 
    /// if we dont have errors - set flag to false.
    /// </summary>
    /// <param name="sender">Ignored.</param>
    /// <param name="e">Ignored.</param>
    private void _ViewModelErrorsChanged(object sender, DataErrorsChangedEventArgs e)
    {
        if ((this.DataContext as INotifyDataErrorInfo).HasErrors)
            _hasErrorsRecentlyChanged = true;
        else
            _hasErrorsRecentlyChanged = false;
    }

    /// <summary>
    /// Iterate over view model visual childrens.
    /// </summary>
    /// <param name="sender">Ignored.</param>
    /// <param name="e">Ignored.</param>
    private void _ViewModelPropertyChanged(object sender, PropertyChangedEventArgs e)
    {
        if ((this.DataContext as INotifyDataErrorInfo).HasErrors)
            _LoopThroughControls(this);
    }

最后添加方法:

    /// <summary>
    /// If we have error and we haven't already set focus - set focus to first control with error.
    /// </summary>
    /// <remarks>Recursive.</remarks>
    /// <param name="parent">Parent element.</param>
    private void _LoopThroughControls(UIElement parent)
    {
        // Check that we have error and we haven't already set focus
        if (!_hasErrorsRecentlyChanged)
            return;

        int count = VisualTreeHelper.GetChildrenCount(parent);

        // VisualTreeHelper.GetChildrenCount for TabControl will always return 0, so we need to 
        // do this branch of code.
        if (parent.GetType().Equals(typeof(TabControl)))
        {
            TabControl tabContainer = ((TabControl)parent);
            foreach (TabItem tabItem in tabContainer.Items)
            {
                if (tabItem.Content == null)
                    continue;

                _LoopThroughControls(tabItem.Content as UIElement);
            }
        }

        // If element has childs.
        if (count > 0)
        {
            for (int i = 0; i < count; i++)
            {
                UIElement child = (UIElement)VisualTreeHelper.GetChild(parent, i);

                if (child is System.Windows.Controls.Control)
                {
                    var control = (System.Windows.Controls.Control)child;

                    // If control have error - we found first control, set focus to it and 
                    // set flag to false.
                    if ((bool)control.GetValue(Validation.HasErrorProperty))
                    {
                        _hasErrorsRecentlyChanged = false;
                        control.Focus();
                        return;
                    }
                }

                _LoopThroughControls(child);
            }
        }
    }