Отслеживание изменения и валидация параметров ObservableCollection

Используя свойства, мы можем отследить их изменения в методе set. Если встает вопрос о валидации, нужно просто реализовать интерфейс IDataErrorInfo и прописать нужную логику вывода ошибок в индексаторе. Собственно, так я и делаю:

MainWindow.xaml:

<Window.DataContext>
    <vm:MainWindowVM/>
</Window.Datacontext>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center" VerticalAlignment="Center">
        <TextBlock Text="Value = "/>
        <TextBox Text="{Binding SingleParameter, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True}" Width="30" Height="20"/>
</StackPanel>

MainViewModel.cs:

public class MainViewModel : ViewModelBase, IDataErrorInfo
{
    public string Error => null;

    public double SingleParameter { get; set; }

    public string this[string propertyName]
    {
        get
        {
            string error = null;

            try
            {
                var parameter = new Parameter("Item", SingleParameter);
            }
            catch (Exception e)
            {
                error = e.Message;
            }

            return error;
        }
    }
}

Parameter.cs:

public class Parameter
{
    private double _value;
    public string Name { get; set; }

    public double Value
    {
        get => _value;
        set
        {
            if (value < 0)
            {
                throw new ArgumentException("Значение не может быть отрицательным");
            }

            _value = value;
        }
    }

    public Parameter(string name, double value)
    {
        Name = name;
        Value = value;
    }
}

И это работает:

Корректный ввод Некорректный ввод

Но как сделать то же самое с параметрами, выводимыми списком ItemTemplate? Я пытаюсь так:

MainWindow.xaml:

<Window.DataContext>
    <local:MainViewModel/>
</Window.DataContext>
<Grid>
    <Border BorderBrush="Black" BorderThickness="1" HorizontalAlignment="Center" VerticalAlignment="Center">
        <ScrollViewer>
            <ItemsControl ItemsSource="{Binding Params}">
                <ItemsControl.ItemTemplate>
                    <DataTemplate>
                        <StackPanel Orientation="Horizontal">
                            <TextBlock Text="{Binding Name}"/>
                            <TextBox Text="{Binding Value, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True}" Width="30" Height="20"/>
                        </StackPanel>
                    </DataTemplate>
                </ItemsControl.ItemTemplate>
            </ItemsControl>
        </ScrollViewer>
    </Border>
</Grid>

MainViewModel.cs:

public class MainViewModel : ViewModelBase, IDataErrorInfo
{
    public string Error => null;

    public ObservableCollection<Parameter> Params { get; set; }

    public MainViewModel()
    {
        Params = new ObservableCollection<Parameter>
        {
            new Parameter("Param1 = ", 10),
            new Parameter("Param2 = ", 20),
            new Parameter("Param3 = ", 30),
            new Parameter("Param4 = ", 40),
            new Parameter("Param5 = ", 50)
        };
    }

    public string this[string propertyName]
    {
        get
        {
            string error = null;

            foreach(var param in Params)
            {
                try
                {
                    var parameter = new Parameter("Item", param.Value);
                }
                catch (Exception e)
                {
                    error = e.Message;
                }
            }

            return error;
        }
    }
}

Но это не работает:

Корректный ввод Некорректный ввод

Как обеспечивается валидация параметров, выведенных с помощью ItemTemplate? Если это делается как-то по-другому, я буду рад любой информации, но так же мне интересно, как правильно непосредственно отследить изменение параметра, если это понадобится, к примеру, для изменения каких-либо свойств, лежащих во вью-модели?


Ответы (1 шт):

Автор решения: aepot

Никогда не работал с IDataErrorInfo, но опыт интересный.

Не знаю, насколько это правильно или нет, но у меня получилось вот так. Дополнительно прикрутил тултип, показывающий, что за ошибка возникла.

<Grid>
    <Border BorderBrush="Black" BorderThickness="1" HorizontalAlignment="Center" VerticalAlignment="Center">
        <ScrollViewer>
            <ItemsControl ItemsSource="{Binding Params}">
                <ItemsControl.ItemTemplate>
                    <DataTemplate>
                        <StackPanel Orientation="Horizontal">
                            <TextBlock Text="{Binding Name}"/>
                            <TextBox Text="{Binding Value, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True}"
                                        ToolTip="{Binding RelativeSource={RelativeSource Self}, Path=(Validation.Errors)[0].ErrorContent}" 
                                        Width="30" Height="20"/>
                        </StackPanel>
                    </DataTemplate>
                </ItemsControl.ItemTemplate>
            </ItemsControl>
        </ScrollViewer>
    </Border>
</Grid>
public class Parameter : ViewModelBase, IDataErrorInfo
{
    private double _value;
    private Dictionary<string, string> _validationErrors;
    public string Name { get; set; }

    public double Value
    {
        get => _value;
        set
        {
            if (value < 0)
                SetValidationError("Значение не может быть отрицательным");
            else
                ClearValidationError();
            _value = value;
        }
    }

    public string Error => null;

    public string this[string propertyName]
    {
        get
        {
            _validationErrors.TryGetValue(propertyName, out string message);
            return message;
        }
    }

    private void ClearValidationError([CallerMemberName] string propertyName = null) 
        => _validationErrors.Remove(propertyName);

    private void SetValidationError(string message, [CallerMemberName] string propertyName = null) 
        => _validationErrors[propertyName] = message;

    public Parameter(string name, double value)
    {
        _validationErrors = new Dictionary<string, string>();
        Name = name;
        Value = value;
    }
}

введите сюда описание изображения

→ Ссылка