Я новичок в WPF, и мне нужно создать небольшое приложение на .NET8, генерирующее текстовые файлы. В пользовательском интерфейсе я хочу иметь два обязательных поля, одно из которых принимает только числа больше 0. Я использовал плавную проверку, подход mvvm и использовал INotifyDataErrorInfo вместе с INotifyPropertyChanged в моей модели представления. Я хочу, чтобы проверка происходила при нажатии кнопки, потере фокуса и т. д. Когда я отлаживаю решение, я получаю ошибки проверки в словаре, но они не отображаются в пользовательском интерфейсе. Я надеюсь, что какая-нибудь добрая душа подскажет мне, что может быть не так.
Модель:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ABCText.FileGeneratorUI.Models
{
public class TargetFile : INotifyPropertyChanged
{
private string _size;
private string _fileName;
public string Size
{
get => _size;
set
{
_size = value;
OnPropertyChanged(nameof(Size));
}
}
public string FileName
{
get => _fileName;
set
{
_fileName = value;
OnPropertyChanged(nameof(FileName));
}
}
public event PropertyChangedEventHandler? PropertyChanged;
protected void OnPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
}
Модель представления
using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using ABCText.FileGeneratorUI.Models;
using ABCText.FileGeneratorUI.Validation;
using FluentValidation.Results;
namespace ABCText.FileGeneratorUI.ViewModels
{
public class MainViewModel : INotifyPropertyChanged, INotifyDataErrorInfo
{
private TargetFile _targetFile;
private readonly TargetFileValidator _validator;
private Dictionary _validationErrors = new Dictionary();
public MainViewModel()
{
_targetFile = new TargetFile();
_validator = new TargetFileValidator();
_targetFile.PropertyChanged += OnTargetFilePropertyChanged;
}
public TargetFile TargetFile
{
get => _targetFile;
set
{
if (_targetFile != null)
_targetFile.PropertyChanged -= OnTargetFilePropertyChanged;
_targetFile = value;
if (_targetFile != null)
_targetFile.PropertyChanged += OnTargetFilePropertyChanged;
OnPropertyChanged(nameof(TargetFile));
Validate();
}
}
public bool HasErrors => _validationErrors.Any();
public void Validate()
{
var validationResult = _validator.Validate(_targetFile);
_validationErrors.Clear();
if (!validationResult.IsValid)
{
foreach (var error in validationResult.Errors)
{
if (!_validationErrors.ContainsKey(error.PropertyName))
_validationErrors.Add(error.PropertyName, new List());
_validationErrors[error.PropertyName].Add(error.ErrorMessage);
ErrorsChanged?.Invoke(this, new DataErrorsChangedEventArgs(error.PropertyName));
}
}
}
protected void OnPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
protected void OnTargetFilePropertyChanged(object? sender, PropertyChangedEventArgs e)
{
Validate();
}
public IEnumerable GetErrors(string? propertyName)
{
return _validationErrors.TryGetValue(propertyName ?? string.Empty, out var errors) ? errors : null;
}
public event PropertyChangedEventHandler? PropertyChanged;
public event EventHandler? ErrorsChanged;
}
}
Представление (xaml):
Вид (код):
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using ABCText.FileGeneratorUI.ViewModels;
using ABCText.FileGeneratorUI.Models;
namespace ABCText.FileGeneratorUI.Views
{
///
/// Interaction logic for MainWindow.xaml
///
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DataContext = new MainViewModel();
}
private void GenerateButton_Click(object sender, RoutedEventArgs e)
{
if (this.DataContext is MainViewModel viewModel)
{
viewModel.Validate();
}
}
}
}
Валидатор:
using ABCText.FileGeneratorUI.Models;
using FluentValidation;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ABCText.FileGeneratorUI.Validation
{
public class TargetFileValidator : AbstractValidator
{
public TargetFileValidator()
{
RuleFor(x => x.Size)
.NotEmpty().WithMessage("Field file size is required.")
.Must(IsIntegerNumberGreaterThanZero).WithMessage("Field must be a number greater than 0.");
RuleFor(x => x.FileName)
.NotEmpty().WithMessage("Field file name is required");
}
private bool IsIntegerNumberGreaterThanZero(string value) => int.TryParse(value, out var result) && result > 0;
}
}
Подробнее здесь: https://stackoverflow.com/questions/791 ... g-up-in-ui