Как добавить валидацию данных к столбцам DataGrid в Avalonia?

Вопрос или проблема

У меня есть приложение .Net 8.0 / AvalonuaUI для редактирования таблиц с использованием CommunityToolkit для MVVM.
Конфигурация таблицы определяется во время выполнения. Поэтому я связываю DataGrid с коллекцией object[], а затем создаю столбцы во время выполнения.

Мне нужно валидировать входные данные в каждом столбце особым образом.
Например, используя минимальное и максимальное значение для числовых параметров (мин и макс также определяются во время выполнения) или TimeSpan.TryParse() для временных параметров.

Как я могу создать пользовательские валидаторы и связать их со столбцами?

На данный момент я создаю столбцы в MainWindow.axaml.cs следующим образом
(я знаю, что это плохо для MVVM, но мне кажется, что это самый простой способ динамически создавать столбцы):

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
    }

    public void BuildDataGridColumns(Parameter[] structure)
    {
        dataGrid.Columns.Clear();

        for(int i = 0; i < structure.Length; i++)
            dataGrid.Columns.Add(CreateColumn(structure[i], i));
    }

    private DataGridColumn CreateColumn(Parameter parameter, int index)
    {
        switch(parameter.Type.Name)
        {
            case nameof(Boolean):
            {
                return new DataGridCheckBoxColumn() {
                    Header = parameter.Name,
                    Binding = new Binding($"[{index}]"),
                    IsReadOnly = false,
                };
            }

            case nameof(Int32):
            case nameof(Single):
            {
                return new DataGridTextColumn() {
                    Header = parameter.Name,
                    Binding = new Binding($"[{index}]"),
                    IsReadOnly = false
                    //Что-то вроде Validator = NumberValidator и т.д.
                };
            }

            default: throw new NotImplementedException();
        }
    }
}

MainWindowViewModel.cs

public partial class MainWindowViewModel : ViewModelBase
{
    public ObservableCollection<ObservableCollection<object?>> Rows { get; set; }
    private MainWindow? _view;

    //Пустой конструктор для предварительного просмотра
    public MainWindowViewModel() : this(null) { }
    public MainWindowViewModel(MainWindow? view)
    {
        _view = view;
        Rows = [[123, 172.32f, true], [321, 3.14f, false], [567, 2.71f, true]];

        Parameter[] structure = new Parameter[] {
            new Parameter("Int", typeof(Int32)),
            new Parameter("Float", typeof(Single)),
            new Parameter("Bool", typeof(Boolean))
        };
        _view?.BuildDataGridColumns(structure);
    }
}

Parameter.cs

public class Parameter
{
    public string Name;
    public Type Type;

    public Parameter(string name, Type type)
    {
        Name = name;
        Type = type;
    }
}

Двусторонняя ссылка в App.axaml.cs

public partial class App : Application
{
    //... сгенерированный код ...

    public override void OnFrameworkInitializationCompleted()
    {
        if(ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
        {
            BindingPlugins.DataValidators.RemoveAt(0);

            MainWindow mainWindow = new MainWindow();
            mainWindow.DataContext = new MainWindowViewModel(mainWindow);
            desktop.MainWindow = mainWindow;
        }

        base.OnFrameworkInitializationCompleted();
    }
}

Ответ или решение

Для добавления валидации данных в столбцы DataGrid в приложении Avalonia, разработанном с использованием .NET 8.0 и CommunityToolkit для MVVM, мы можем создать кастомные валидаторы и связанных их со столбцами динамически. Ниже представлено подробное описание процесса, включая реализацию логики валидации для различных типов данных.

Шаг 1: Определение валидатора

Первое, что вам нужно сделать, это определить интерфейс валидатора и его реализацию для различных типов данных. Например, мы можем создать валидатор для целых чисел и чисел с плавающей запятой.

public interface IValueValidator
{
    bool Validate(object value);
    string ErrorMessage { get; }
}

public class NumberRangeValidator : IValueValidator
{
    private readonly int _min;
    private readonly int _max;

    public NumberRangeValidator(int min, int max)
    {
        _min = min;
        _max = max;
    }

    public bool Validate(object value)
    {
        return value is int intValue && intValue >= _min && intValue <= _max;
    }

    public string ErrorMessage => $"Значение должно быть в пределах от {_min} до {_max}.";
}

public class FloatRangeValidator : IValueValidator
{
    private readonly float _min;
    private readonly float _max;

    public FloatRangeValidator(float min, float max)
    {
        _min = min;
        _max = max;
    }

    public bool Validate(object value)
    {
        return value is float floatValue && floatValue >= _min && floatValue <= _max;
    }

    public string ErrorMessage => $"Значение должно быть в пределах от {_min} до {_max}.";
}

Шаг 2: Изменение класса Parameter

Обновите класс Parameter для хранения информации о валидаторе:

public class Parameter
{
    public string Name { get; }
    public Type Type { get; }
    public IValueValidator? Validator { get; }

    public Parameter(string name, Type type, IValueValidator? validator = null)
    {
        Name = name;
        Type = type;
        Validator = validator;
    }
}

Шаг 3: Создание столбцов с валидатором

При создании столбцов в вашем методе CreateColumn вы можете добавить логику для валидации:

private DataGridColumn CreateColumn(Parameter parameter, int index)
{
    var column = new DataGridTextColumn()
    {
        Header = parameter.Name,
        Binding = new Binding($"[{index}]"),
        IsReadOnly = false
    };

    // Добавление обработки валидации
    column.CellStyle = new Style(typeof(DataGridCell))
    {
        Setters =
        {
            new Setter(DataGridCell.BackgroundProperty, new SolidColorBrush(Colors.White))
        },
        Triggers =
        {
            new DataTrigger
            {
                Binding = new Binding($"[{index}]"),
                Value = null,
                Setters =
                {
                    new Setter(DataGridCell.BackgroundProperty, new SolidColorBrush(Colors.Red)) // Zдесь можно поменять цвет
                }
            },
            new DataTrigger
            {
                Binding = new MultiBinding
                {
                    Converter = new ValidationConverter(parameter.Validator), // Вам нужно реализовать под вашу логику
                    Bindings =
                    {
                        new Binding($"[{index}]")
                    }
                },
                Value = false,
                Setters =
                {
                    new Setter(DataGridCell.BackgroundProperty, new SolidColorBrush(Colors.Red)) // Zнак неверного значения
                }
            }
        }
    };

    return column;
}

Шаг 4: Реализация логики валидации в конвертере

Чтобы связать значение с вашим валидатором, создайте IValueConverter, который будет использовать валидатор для проверки значения:

public class ValidationConverter : IMultiValueConverter
{
    private readonly IValueValidator _validator;

    public ValidationConverter(IValueValidator validator)
    {
        _validator = validator;
    }

    public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
    {
        var value = values[0];
        return _validator.Validate(value);
    }

    public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}

Пример использования

Теперь вы можете создать экземпляр вашего параметра с использованием валидаторы при инициализации:

Parameter[] structure = new Parameter[]
{
    new Parameter("Int", typeof(Int32), new NumberRangeValidator(0, 100)),
    new Parameter("Float", typeof(Single), new FloatRangeValidator(0.0f, 100.0f)),
    new Parameter("Bool", typeof(Boolean))
};

Заключение

Таким образом, вы сможете динамически добавлять валидацию данных в столбцы вашего DataGrid в приложении Avalonia, используя объектно-ориентированный подход и принципы MVVM. Важно помнить, что для каждой строки, ячейки и ввода данных можно использовать кастомные валидаторы, что позволит предотвратить неправильный ввод в вашем приложении. Это решение обеспечит более высокий уровень надежности и улучшит пользовательский интерфейс вашего приложения.

Оцените материал
Добавить комментарий

Капча загружается...