0

Ok let's say we have a Parent Window bound to a DataContext now assume we fire a Child Window and set its data context to that of the parent window. Now the items in the child window are bound to the data context in TwoWay mode. When the user changes something in this child window the data in the source viewmodel will be updated. But what if the user closes the window with DialogResult == false? How to rollback the data to its original?

For now I'm binding the data in OneWay mode and applying the changes only when the DialogResult == true. But I guess there must be a more elegant way to do this.

Vahid
  • 5,144
  • 13
  • 70
  • 146
  • If you define a VM property or command corresponging to your confirmation event, then you could move all the `OnPropertyChanged` from the setters of the items to that point. –  Jul 18 '16 at 09:18
  • @MachineLearning Can you please give me an example for this. This seems like a clean solution but I need some implementation example to see how it works. – Vahid Jul 18 '16 at 09:19

3 Answers3

3

IEditableObject

In the same way that WPF can use System.ComponentModel.INotifyPropertyChanged you can also use the interface System.ComponentModel.IEditableObject. This interface has the following useful methods:

Prior to displaying your window, call BeginEdit() on your datacontext. Now any changes reflect via the usual INotifyPropertyChanged so that the parent receives updates as usual.

If the user cancels, then call CancelEdit() at which point your viewmodel should rollback the changes. The free set of steak knives is that your parent window is informed of the rollback via INotifyPropertyChanged thus "reverting" the parent window too.

Otherwise call EndEdit() which honestly isn't required as the changes are done already.

Obviously you would need to remember somehow the prior values so that you can revert any changes.

IRevertibleChangeTracking

I've just read about System.ComponentModel.IRevertibleChangeTracking which offers the methods AcceptChanges() and RejectChanges(). Though arguably this is the interface to use for accepting and rolling back changes, it's not entirely clear whether changes made in the interim should be broadcast in the WPF scenario. Perhaps someone can help me here.

Tell me more...

Community
  • 1
  • 1
  • Thanks MickyD, so in my Viewmodel I will need to keep the original data for all the properties as separate properties/fields? – Vahid Jul 18 '16 at 09:22
  • Thank you. This is working well. Although one needs to keep track of all the properties in the viewmodel to make a backup but it is still easier than other methods. – Vahid Jul 18 '16 at 09:37
  • @Vahid You're welcome good sir. There might be some trick. Maybe have a loop to reflect over all your `public` properties then later reflect again to see what's changed? –  Jul 18 '16 at 09:41
  • I just noticed a weird thing. When I use two radio buttons, with the `IsChecked` bound to a property with converter, and I cancel the first time, it works. The next time I cancel it also works. The third time when I change it is locked on one radio button and won't change! – Vahid Jul 18 '16 at 09:52
  • This is odd, it is not because of the `BeginEdit()` methods. I'll post it as another question. – Vahid Jul 18 '16 at 10:01
  • http://stackoverflow.com/questions/38434625/odd-behavior-when-trying-to-change-a-bound-radiobutton-in-wpf – Vahid Jul 18 '16 at 10:40
  • 1
    @Vahid Interesting. Feel free to post that as a _new question_ –  Jul 18 '16 at 13:04
  • 1
    Thanks MickyD, I did. It seems it was because of the Converter I used. – Vahid Jul 18 '16 at 13:05
  • Hi, can you please see this? http://stackoverflow.com/questions/38544631/datagrid-causes-beginedit-method-to-run-twice – Vahid Jul 23 '16 at 17:32
  • 1
    @Vahid very good. For a datagrid I too recommend the other post but otherwise I stand by my solution. Glad you got it going –  Jul 24 '16 at 02:50
1

Change the UpdateSourceTrigger on the binding to be explicit and call the UpdateSource method only when the OK button is clicked.

UpdateSourceTrigger

Mvarta
  • 518
  • 3
  • 7
  • Thanks Mvarta I was just reading about this method but I couldn't find any good code example. How can I do it for example for two radio buttons? – Vahid Jul 18 '16 at 09:17
1

For example, you can bind the confirmed values as one-way, readonly in the main view and the uncommitted ones to the dialog text boxes.

ViewModel
    class MyVM : MVVM.ViewModel.ViewModelBase
    {
        private string name1;
        public string Name1
        {
            get { return name1; }
            set {
                name1 = value;
                OnPropertyChanged(() => Name1);
            }
        }
        string name1Conf;
        public string Name1Conf
        {
            get { return name1Conf; }
        }
        private string name2;
        public string Name2
        {
            get { return name2; }
            set
            {
                name2 = value;
                OnPropertyChanged(() => Name2);
            }
        }
        string name2Conf;
        public string Name2Conf
        {
            get { return name2Conf; }
        }

        private bool commitMe;

        public bool CommitMe
        {
            get { return commitMe; }
            set {
                commitMe = value;
                OnPropertyChanged(() => CommitMe);
                if (commitMe)
                {
                    DoCommit();
                } 
            }
        }

        private void DoCommit()
        {
            name1Conf = name1;
            name2Conf = name2;
            OnPropertyChanged(() => Name1Conf);
            OnPropertyChanged(() => Name2Conf);
        }
    }

CodeBehind

private void button_Click(object sender, RoutedEventArgs e)
{
    var win2 = new WPFDialog();
    win2.DataContext = myVM;
    var res = win2.ShowDialog();
    myVM.CommitMe = res == true;
}