-2

I do have a Winform which consists of multiple fields representing the Candidate Entity below. I have an Edit button, which will enable the fields for the user to update this Candidate record and reflect the Entity accordingly. So before closing the Form, I would like to warn the user of unsaved data! Either Save or Cancel? If Cancel is selected reverts the Forms fields data to the previous Entity Data.

Is there an elegant way of doing this mechanism?

public partial class Candidate
    {

        public int Id { get; set; }
        public string FirstName { get; set; } = null!;
        public string? MiddleName { get; set; }
        public string LastName { get; set; } = null!;
        public DateTime? DateOfBirth { get; set; }
        public string? Gender { get; set; }
        public string? Nationality { get; set; }
        public string? Education { get; set; }

    }
Unis
  • 365
  • 1
  • 4
  • 13
  • Are you perhaps looking for a `MessageBox`? See: https://learn.microsoft.com/en-us/dotnet/api/system.windows.forms.messagebox?view=windowsdesktop-6.0 – Ibrennan208 Aug 15 '22 at 17:50
  • No actually, I know how to do that.... but how to know if the Entity has unsaved changes then I can warn the user through a message box. – Unis Aug 15 '22 at 17:52

2 Answers2

1

There are multiple ways to do this out there, this is a basic example of how to approach the idea. You are basically asking "How do I keep track of the fact that at least one of my properties has changed, and how do I revert it if the user decides against saving".

For your current example, you could add a flag in your class that also gets updated any time a property gets updated.

See this:

            private int id;
            public int Id { 
                get => id;
                set
                {
                    HasChanged = true;
                    id = value;
                }
            }

            public bool HasChanged = false;

Then if the user tries to navigate away, you can check the boolean to see if it needs to be interrupted and decide if you need to restore data.

There are many ways to do this, this is just one example of how you could start to implement it. It's a very common approach in MVVM to have a view model with a dirty flag that can reset if the user chooses. Here is another example on StackOverflow that could help you: MVVM - implementing 'IsDirty' functionality to a ModelView in order to save data

To further build on this, I will show you an example of a base class I use for some of my work and how I implement an IsDirty flag, to indicate if any of my information has changed.

Here is my BaseViewModel:

public class BaseViewModel : INotifyPropertyChanged
    {
        protected bool isDirty = false;
        public bool IsDirty
        {
            get { return isDirty; }
            set { SetProperty(ref isDirty, value); }
        }

        protected bool isBusy = false;
        public bool IsBusy
        {
            get { return isBusy; }
            set { SetProperty(ref isBusy, value); }
        }

        protected string title = string.Empty;
        public string Title
        {
            get { return title; }
            set { SetProperty(ref title, value); }
        }

        protected bool SetProperty<T>(ref T backingStore, T value,
            [CallerMemberName] string propertyName = "",
            Action onChanged = null)
        {
            if (EqualityComparer<T>.Default.Equals(backingStore, value))
                return false;

            backingStore = value;
            onChanged?.Invoke();
            OnPropertyChanged(propertyName);

            // if the property actually changed, IsDirty is true => used to determine if saving is needed
            if (propertyName != nameof(IsDirty) && propertyName != nameof(IsBusy))
            {
                IsDirty = true;
            }
            return true;
        }

        #region INotifyPropertyChanged
        public event PropertyChangedEventHandler PropertyChanged;
        protected void OnPropertyChanged([CallerMemberName] string propertyName = "")
        {
            var changed = PropertyChanged;
            if (changed == null)
                return;

            changed.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }
        #endregion
    }

And here is an example of an ItemDetailViewModel(mostly autogenerated from Microsoft) which implements that BaseViewModel:

    public class ItemDetailViewModel : BaseViewModel
    {
        private string itemId;
        private string text;
        private string description;
        public string Id { get; set; }

        public string Text
        {
            get => text;
            set => SetProperty(ref text, value);
        }

        public string Description
        {
            get => description;
            set => SetProperty(ref description, value);
        }

        public string ItemId
        {
            get
            {
                return itemId;
            }
            set
            {
                itemId = value;
                LoadItemId(value);
            }
        }

        public async void LoadItemId(string itemId)
        {
            IsBusy = true;
            try
            {
                var item = await DataStore.GetItemAsync(itemId);
                Id = item.Id;
                Text = item.Text;
                Description = item.Description;
            }
            catch (Exception)
            {
                Debug.WriteLine("Failed to Load Item");
            }
            finally
            {
                IsBusy = false;
                IsDirty = false;
            }
        }

Now, if any of my ItemDetailViewModel objects ever have a property modified, that specific object's IsDirty flag would be set to true until I set it back to false. Notice in this example, I actually have an item that gets loaded, and I copy those properties into a "View Model" of that item. Ultimately, when I go to save, I will check to see if the view model is dirty, and if so, I would then update the existing item.

Perhaps implementing something like this in your project would help you. It may add to the amount of code you are writing when it comes to the property setting, but ultimately you may find it helps with managing the objects.

Ibrennan208
  • 1,345
  • 3
  • 14
  • 31
  • Well, this certainly does the trick if I want to track one or a few variables. But for many database Entities and their variables, it will be extreme to add a flag to each and every variable for each entity. – Unis Aug 15 '22 at 18:23
  • 1
    *it will be extreme to add a flag to each and every variable for each entity* - it may seem extreme, but you have to compare it to other solutions. How much code does your current solution use? Check out the link posted as well, and perhaps `INotifyPropertyChanged`: https://learn.microsoft.com/en-us/dotnet/api/system.componentmodel.inotifypropertychanged?view=net-6.0 – Ibrennan208 Aug 15 '22 at 19:01
  • 1
    I've added some additional examples for you, in hopes to clarify how it may help. – Ibrennan208 Aug 15 '22 at 19:09
0

Just do not call SaveChanges(),

China Syndrome
  • 953
  • 12
  • 24
  • Eventually, I would call `SaveChanges()` if the user selects Yes to save the changes but I need to warn the user before they move away from the From. – Unis Aug 15 '22 at 18:25