I have a model

public partial class Patient : GenericEntity
{
    [ObservableProperty]
    private int genderId;

    [ObservableProperty]
    [NotifyPropertyChangedFor(nameof(Crucials))]
    private string lastName = string.Empty;

    [ObservableProperty]
    [NotifyPropertyChangedFor(nameof(Crucials))]
    private string firstName = string.Empty;

    [ObservableProperty]
    private string fatherName = string.Empty;

    [ObservableProperty]
    [NotifyPropertyChangedFor(nameof(Crucials))]
    [NotifyPropertyChangedFor(nameof(Age))]
    private DateTime _DOB = DateTime.Now;

    [NotMapped]
    public int Age
    {
        get
        {
            //DateOnly currentDate = DateOnly.FromDateTime(DateTime.Now);
            DateTime currentDate =DateTime.Now;
            int age = currentDate.Year - DOB.Year;

            // Check if the current date is before the birth date
            if (currentDate < DOB.AddYears(age))
            {
                age--;
            }

            return age;
        }
    }

    [ObservableProperty]
    private string _address = "";
    [ObservableProperty]
    private string _city = "";
    [ObservableProperty]
    private string _country = "";
    [ObservableProperty]
    private string _tel1 = "";
    [ObservableProperty]
    private string _tel2 = "";
    [ObservableProperty]
    private string _email = "";
  
   //(15 more fields are following all of them decorated with [ObservableProperty])
}

The viewmodel is as follows

public partial class PatientViewModel : ObservableObject { private bool _patientPropertyChanged = false;

private AppDbContext _db;

private readonly PatientRepository _patientRepository;
private readonly NATreeNodeRepository _natreenodeRepository;

private Patient? patient;

[ObservableProperty]
private int _patientId = 0;

[RelayCommand(CanExecute = nameof(CanSaveChanges), IncludeCancelCommand = true)]
private async Task SaveChanges(CancellationToken token)
{
    var hasChanges = _db.ChangeTracker.HasChanges();
    if (hasChanges)
    {
        try
        {
            // Update the Patient's registration date to DateTime.Now
            if (Patient != null)
            {
                Patient.UpdatedOn = DateTime.Now;
            }

            await _db.SaveChangesAsync();
            MessageBox.Show("Changes successfully saved to the database!");
        }
        catch (System.Exception ex)
        {
            MessageBox.Show(ex.Message);
        }
    }

    MessageBox.Show("Saved changes");
}

}

private bool CanSaveChanges() => PatientPropertyChanged;



public PatientViewModel(int patientId)
{
    _db = new();

    _patientRepository = new PatientRepository(_db);
    _natreenodeRepository = new NATreeNodeRepository(_db);

    PatientId = patientId;

    InitializeAsync(PatientId);

    _patientPropertyChanged = false;
}

private async void InitializeAsync(int patientId)
{
    try
    {
                                                                                        // Retrieve the patient with the given ID from the repository
        Patient = await Task.Run(() => _patientRepository.Get(patientId)); // Replace with your repository method

        // Check if patient is null or doesn't exist
        if (Patient == null)
        {
            Patient = new()
            {
            };
            _patientRepository.Add(Patient);
        }
    }
    catch (Exception ex)
    {
        Debug.WriteLine("Error: " + ex.Message);
    }
}

}

The PersonView window contains several controls (one for each of the person's properties) and a, initially disabled, SaveButton bound to a RelayCommand SaveCommand

My question is how can someone enable or disable the save button when any of the Person's properties is changed by the user on this window.

As I am stuck I can not find any solution to this question.

There is a solution if the Person contains 2-3 properties. But when it contains many then I can not come up with a clever solution that could avoid repeating blocks of code

Thanks in advance

2

There are 2 best solutions below

5
On BEST ANSWER

Solution 1 - Use Database

As I understand the capabilities of you Database you may delegate this to your database, as _db.ChangeTracker.HasChanges() seams to check if there are changes. So you may change CanSaveChanges()to

private bool CanSaveChanges() => _db.ChangeTracker.HasChanges();

Solution 2 - Use PropertyChanged Event

Otherwise if you database can not check for changes this you may use the PropertyChanged Event like:

// Properties for Patient and Changed State
[ObservableProperty] private bool _patientPropertyChanged;
[ObservableProperty] private Patient _patient;

public bool CanSaveChanges() => PatientPropertyChanged;

private async void InitializeAsync(int patientId)
{
    try
    {
         if (Patient == null)
        {
            Patient = new()
            {
            };
            _patientRepository.Add(Patient);
        }

        //Add Event Handle to set changed
        Patient.PropertyChanged += (s, e) => 
        {
            //You may filter here for relevant changes
            PatientPropertyChanged = true;
        };
    }
    catch (Exception ex)
    {
        Debug.WriteLine("Error: " + ex.Message);
    }
}

[RelayCommand(CanExecute = nameof(CanSaveChanges), IncludeCancelCommand = true)]
private async Task SaveChanges(CancellationToken token)
{
    var hasChanges = _db.ChangeTracker.HasChanges();
    if (hasChanges)
    {
        //..
    }
    //Reset changed Satte
    PatientPropertyChanged = false;
}
1
On

You should raise the CanExecuteChanged event for the command whenever the PatientPropertyChanged property is set.

In the MVVM toolkit, you can do this by applying the [NotifyCanExecuteChangedFor] attribute to the backing field for the property:

[ObservableProperty]
[NotifyCanExecuteChangedFor(nameof(SaveChangesCommand))]
private bool _patientPropertyChanged;