Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Open/Close View from ViewModel

I have an AddClientViewModel which is referenced by 2 views (AddClientView and SuggestedAddressesView). The AddClientView is a form which has a field for an address. The form has a validate button which validates the entered address using Geocoding. If more than one address is returned, the SuggestedAddressesView opens.

Here is how I am currently doing it:

AddClientViewModel:

    private void ValidateExecute(object obj)
    {
        SuggestedAddresses = new ObservableCollection<DBHelper.GeocodeService.GeocodeResult>(GeoCodeTest.SuggestedAddress(FormattedAddress));

        ....

        if (SuggestedAddresses.Count > 0)
        {
            var window = new SuggestedAddressesView(this);
            window.DataContext = this;
            window.Show();
        }
    }

Here is the SuggestedAddressesView constructor where AddClientViewModel inherits from ViewModelBase

    public SuggestedAddressesView(ViewModelBase viewModel)
    {
        InitializeComponent();
        viewModel.ClosingRequest += (sender, e) => this.Close();
    }

The other problem I am having is when I call OnClosingRequest() from the AddClientViewModel...both the AddClientView and SuggestedAddressesView closes. I know this happens because both views reference the same ViewModel. This is not the behaviour I want. I would like to be able to independently close either Window.

Is opening a View from the ViewModel proper MVVM structure and how would I go about being able to close windows independently?

like image 648
francisg3 Avatar asked Aug 26 '13 00:08

francisg3


People also ask

Can a Viewmodel have multiple views?

While a view should only have one viewmodel, a single viewmodel might be used by multiple views (imagine a wizard, for example, that has three views but all bind to the same viewmodel that drives the process).

How do I connect Viewmodel view?

This can be done directly in xaml (the View just instances the ViewModel directly). This can be done in the View's constructor ( this. DataContext = new MyViewModel(); )


1 Answers

As soon as you refer to UI elements(In this case the View) from the VM, you're going against suggested MVVM Guidelines. With just that we can know creating the Window object in the VM is wrong.

So now onto rectifying this:

  • Firstly try to keep a 1 View <-> 1 VM in your application. It's cleaner and allows you to switch out View implementations with the same logic very easily. Adding multiple View's to the same VM even if not "ground-breaking" just makes it clumsy.
  • So now you got AddClientView and SuggestedAddressesView with their own VM. Great!

Implementing a View Open/Close from the VM:

  • Since we cannot access the View directly from our VM(to comply with standards), we can use approaches such as using a Messenger(MVVM Light), EventAggregator(PRISM) and so on to send a "message" from the VM to the View when you need to open/close a View and do the actual operation in the View.
  • This way the VM just initiates the message and can be unit-tested fine for the same operation and does not reference any UI elements.

Using a "Messenger" approach to handle View open:

  • As per your Logic, it is the AddClientViewModel which would have to ask for the SuggestedAddressesView to be opened.
  • Thus when you detect SuggestedAddresses.Count > 0, you would send a message to the AddClientView asking it to open up the SuggestedAddressesView.
  • In AddClientView.xaml.cs upon receiving this message, you would do what you're currently doing in the VM. Create an object of SuggestedAddressesView and call .Show() on it.
  • One extra step you would add in the above step's process is to assign the DataContext of SuggestedAddressesView as SuggestedAddressesViewModel.

That's it. Now what you have is, when AddClientViewModel wants SuggestedAddressesView shown, it sends a message to it's own View and the View in-turn creates and shows the SuggestedAddressesView. This way the VM does not reference any View's and we keep to holding MVVM standards.

Using a "Messenger" approach to handle View close:

  • Closing a View is pretty simple. Again when you need to close the View from the VM, you send a message to it's own View asking for it to be closed.
  • On receiving this message, the View pretty much closes itself via .Hide() / .Close() or however else you want to get rid of it.

In this each VM handles it's own View's closing and you don't have any inter-connected dependencies.

You can use this as a start point to guide you in handling "messages" for this approach. it has an attached download you can get and see how the Messenger works. This is with MVVM Light, if you do not use it or use something else/ your own MVVM implementation, use it as a guide to help get to what you need.

like image 126
Viv Avatar answered Oct 13 '22 19:10

Viv