Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to keep collections of viewmodels and models in sync

I'm using the wpf toolkit datagrid to display an observable collection of AccountViewModels.

The thing is when I delete an account from the grid, I want it removed from the ObservableCollection - to give the user visual feedback, but i want the underlying list of Account models to remain the same, just with an 'IsDeleted' flag set on the Account model.

Then whenever the changes are committed, my service knows which accounts to add/update or delete in the database.

Im subscribing to the CollectionChanged event:

AccountViewModels.CollectionChanged += AccountsChanged;

and then setting the viewmodels' model isdeleted flag to true whenever something is removed:

private void AccountsChanged(object sender, NotifyCollectionChangedEventArgs e)
    {
        if (e.NewItems != null)
        {
            foreach (AccountViewModel model in e.NewItems)
            {
                model.PropertyChanged += accountPropertyChanged;
                model.Account.IsNew = true;
            }
        }
        if (e.OldItems != null)
        {

            foreach (AccountViewModel model in e.OldItems)
            {
                model.PropertyChanged -= accountPropertyChanged;
                model.Account.IsDeleted = true;
            }
        }
    }

but obviously this then removes it from the observable collection. So when I come to commit the changes, there will be no accounts with IsDeleted flag set. i.e. they will have already been removed.

 foreach (AccountViewModel acc in m_ViewModel.AccountViewModels)
        {
            WorkItem workItem = null;
            if(acc.Account.IsNew)
                workItem = new WorkItem("Saving new account: " + acc.AccountName, "Saving new account to the database", () => Service.AddAccount(acc.Account));
            else if (acc.Account.IsDeleted)
                workItem = new WorkItem("Removing account: " + acc.AccountName, "Setting account inactive in the database", () => Service.RemoveAccount(acc.Account));
            else if(acc.Account.IsDirty)
                workItem = new WorkItem("Updating account: " + acc.AccountName, "Updating account in the database", () => Service.UpdateAccount(acc.Account));

            workItems.Add(workItem);

        }

So does this mean I need to maintain two lists, one list of account models and the other an observable collection of accountviewmodels? This just seems nasty and there must be a better way of doing this.

like image 734
cjroebuck Avatar asked Jan 26 '10 11:01

cjroebuck


People also ask

Can ViewModels communicate with each other?

We know that global command is triggered by binder sending events into event queue, hence, ViewModels attached with binders are able to communicate with each other by global command.

Should ViewModel know about model?

Models are just the plain data, and a ViewModel is something that acts like a padding in between the two, that it should get information from the Model and pass it onto the View, and the View should know how to present it.

What logic should be in ViewModel?

ViewModel: ViewModel is the middle layer between the view and model. ViewModel contains the business logic, which manipulates the row data to show in the view. Any kind of function and methods should be in the view model. The iNotifyPropertyChanged interface is used in the ViewModel to achieve two-way binding.

What is the use of ViewModel in MVVM?

The viewmodel of MVVM is a value converter, meaning the viewmodel is responsible for exposing (converting) the data objects from the model in such a way that objects are easily managed and presented. In this respect, the viewmodel is more model than view, and handles most if not all of the view's display logic.


2 Answers

I don't think you can do this any better with ObservableCollection, as that class holds its own internal list of objects.

However, if you implement a custom Collection that implements INotifyCollectionChanged and INotifyPropertyChanged, you can let it wrap and filter your source collection.

It could filter on the IsDeleted flag so that these are not visible.

Whenever a user removes an item, you can modify the Domain Model directly, setting the IsDeleted flag to true. However, you will still need an eventing mechanism to be able to raise the appropriate events, but with that approach, you only have a single collection of items.

The custom Collection would simply be a Projection over the Domain Model, with added events.

like image 97
Mark Seemann Avatar answered Oct 21 '22 09:10

Mark Seemann


You can find an example here : http://blog.lexique-du-net.com/index.php?post/2010/03/02/M-V-VM-How-to-keep-collections-of-ViewModel-and-Model-in-sync

Hope this help

like image 26
Jonathan ANTOINE Avatar answered Oct 21 '22 09:10

Jonathan ANTOINE