Open/Close View from ViewModel
Asked Answered
A

3

7

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?

Anaplastic answered 26/8, 2013 at 0:46 Comment(0)
M
5

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.

Mazonson answered 26/8, 2013 at 9:5 Comment(2)
Alright, that makes sense! The issue I am having is that I'd like the information gathered from SuggestedAddressesView to be passed to the AddClientViewModel (hence why I was using 1 VM for 2 Views). The reason I am doing this is because the Address selected in the SuggestedAddressesView is assigned to a Client defined in the AddClientViewModel.Anaplastic
@francisg3 look at the example link I posted. If you get the example there, The second Window opened Modal/Non-Modal passes information back to the MainWindow. This is the process you would be using for your requirement too. You'd pretty much use Messenger to send a message(here the message will be the data you want to send from SuggestedAddressViewModel to AddClientViewModel)Mazonson
S
0

you can use RelayCommand so that you send the parameter as follows:

Command="{Binding CloseWindowCommand, Mode=OneWay}" 
CommandParameter="{Binding ElementName=TestWindow}"

By using this you can close the individual views.

Example:

public ICommand CloseCommand
    {
        get
        {
            return new RelayCommand(OnClose, IsEnable);
        }
    }

public void OnClose(object param)
    {
       AddClientView/SuggestedAddressesView Obj = param as AddClientView/SuggestedAddressesView;
       obj.Close();
    }
Suffer answered 26/8, 2013 at 8:59 Comment(0)
G
0

To open window from ViewModel:

Create NavigationService.cs class for opening window: Let NavigationService.cs

Now put following code in that class file.

   public void ShowWindow1Screen(Window1ViewModel window1ViewModel)
       {
           Window1= new Window1();
           Window1.DataContext = window1ViewModel;
           Window1.Owner = Window1View;
           Window1.ShowDialog();
       }

then. Create instance of NavigationService.cs class MainWindowViewModel file. Then

Window1ViewModel window1ViewModel = new Vindow1ViewModel();
window1ViewModel.Name = MainWindowTextValue;
NavigationService navigationService = new NavigationService();
navigationService.ShowWindow1Screen(window1ViewModel);
Garfish answered 13/5, 2015 at 8:50 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.