Получить фокус обратно на элемент управления при использовании INotifyDataErrorInfo для проверки - PullRequest
1 голос
/ 09 декабря 2011

Я использую простые проверки с использованием реализации INotifyDataErrorInfo в silverlight.

При отправке я проверяю все свойства, чтобы показать все ошибки.

Мне нужно вернуть фокус на первый элемент управления с ошибкой проверки, когда проверка происходит.

У нас есть способ сделать это?Есть предложения?

1 Ответ

0 голосов
/ 21 августа 2012

Лучше поздно, чем никогда :)

Я реализовал это поведение.

Сначала вам нужно подписаться на методы ViewModel ErrorsChanged и PropertyChanged. Я делаю это в моем конструкторе:

    /// <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);
            }
        }
    }
...