Validation.ErrorEvent не возвращается, когда элемент с ошибкой удаляется - PullRequest
0 голосов
/ 24 октября 2019

Я должен показать список, где каждый элемент будет проверен. Я подписан на Validation.ErrorEvent на верхнем уровне для наблюдения за детьми.

Когда я удаляю элемент с ошибкой проверки из списка, это событие не возникает.

В приведенном ниже примере у меня есть 3 TextBox на экране, каждый привязан к int свойству. Ввод неправильного значения вызовет событие (Title будет изменено на "+"), после исправления значения событие будет инициировано один раз (Title будет изменено на "-").

Однако при удалении TextBox при наличииошибка не будет возникать событие (для очистки) и Title остаться "+":

Как я могу это исправить? В идеале я хочу, чтобы это событие автоматически вызывалось до того, как произойдет удаление.

Обратите внимание: в реальном проекте существует сложная иерархия моделей представлений, такие решения, как «установить Title в методе удаления», требуют мониторинга для подчиненных. просматривает и распространяет эту информацию по всей иерархии, чего я хотел бы избежать. Я бы предпочел решение только для просмотра.

MCVE:

public partial class MainWindow : Window
{
    public ObservableCollection<VM> Items { get; } = new ObservableCollection<VM> { new VM(), new VM(), new VM() };

    public MainWindow()
    {
        InitializeComponent();
        AddHandler(Validation.ErrorEvent, new RoutedEventHandler((s, e) =>
            Title = ((ValidationErrorEventArgs)e).Action == ValidationErrorEventAction.Added ? "+" : "-"));
        DataContext = this;
    }

    void Button_Click(object sender, RoutedEventArgs e) => Items.RemoveAt(0);
}

public class VM
{
    public int Test { get; set; }
}

xaml:

<StackPanel>
    <ItemsControl ItemsSource="{Binding Items}" Height="200">
        <ItemsControl.ItemTemplate>
            <DataTemplate>
                <TextBox Text="{Binding Test, NotifyOnValidationError=True, UpdateSourceTrigger=PropertyChanged}" />
            </DataTemplate>
        </ItemsControl.ItemTemplate>
    </ItemsControl>
    <Button Content="Remove first" Click="Button_Click" />
</StackPanel>

1 Ответ

1 голос
/ 24 октября 2019

Через некоторое время у меня есть рабочее решение, с которого вы могли бы начать. Как уже упоминалось Эдом, You're using the UI as a data structure, which is never a good idea. The MVVM way to do validation is IDataErrorInfo, это действительно так, и вы должны реализовать интерфейс IDataErrorInfo для обработки этих ошибок.

В другом примечании, вот что я сделал, чтобы заставить его работать. Я обрабатываю событие CollectionChanged для ObservableCollection ваших VM. Когда коллекция изменяется, вам нужно найти элемент, который был фактически удален, если он найден, мы можем попытаться очистить его ValidationError объект для самого этого элемента.

Вот класс -

public partial class MainWindow : Window
    {
        public ObservableCollection<VM> Items { get; } = new ObservableCollection<VM> { new VM(), new VM(), new VM() };                             

        public MainWindow()
        {
            InitializeComponent();

            Items.CollectionChanged += Items_CollectionChanged;

            AddHandler(Validation.ErrorEvent, new RoutedEventHandler((s, e) =>
            Title = ((ValidationErrorEventArgs)e).Action == ValidationErrorEventAction.Added ? "+" : "-"));

            DataContext = this;        
        }

        private void Items_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
        {
            if (e.Action == System.Collections.Specialized.NotifyCollectionChangedAction.Remove) {
                foreach (TextBox tb in FindVisualChildren<TextBox>(this))
                {
                    if(tb.DataContext == e.OldItems[0])
                    {
                        Validation.ClearInvalid(tb.GetBindingExpression(TextBox.TextProperty));
                        break;
                    }                    
                }
            }            
        }

        private void Button_Click(object sender, RoutedEventArgs e) => Items.RemoveAt(0);

        public static IEnumerable<T> FindVisualChildren<T>(DependencyObject depObj) where T : DependencyObject
        {
            if (depObj != null)
            {
                for (int i = 0; i < VisualTreeHelper.GetChildrenCount(depObj); i++)
                {
                    DependencyObject child = VisualTreeHelper.GetChild(depObj, i);
                    if (child != null && child is T)
                    {
                        yield return (T)child;
                    }

                    foreach (T childOfChild in FindVisualChildren<T>(child))
                    {
                        yield return childOfChild;
                    }
                }
            }
        }

    }
    public class VM
    { 
        public int Test { get; set; }

    }

Хлеб с маслом для этой работы - Validation.ClearInvalid(tb.GetBindingExpression(TextBox.TextProperty));, который фактически удаляет все ValidationError объекты из BindingExpressionBase объект, который в данном случае является TextBox.TextProperty.

Примечание: здесь не было никакой проверки ошибок, вы можете сделать это.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...