View Deactivation from ViewModel

Topics: Prism v4 - WPF 4
Jan 9, 2012 at 9:45 AM

I have a region which contains multiple instances of the Patient View. And I am wanting to deactivate Patient Views from the ViewModel, based on which ever one the user clicks close on. So I have the following bit of code, but wondering how I access the correct view when trying to remove. Obviously here I am just removing the first in the views collection.

 

 IRegion region = _regionManager.Regions[RegionNames.SelectedPatientRegion];

region.Deactivate(region.Views.ElementAt(0));

Developer
Jan 9, 2012 at 5:13 PM

Hi,

Based on my understanding, if your region only shows one active view (for example, a ListControl in which you can have only one item selected at the same time or a TabControl) you can obtain this view from the ActiveViews collection of the region and do something like this:

IRegion region = regionManager.Regions["MainRegion"];
region.Deactivate(region.ActiveViews.FirstOrDefault());

If this is not the case and your region can shown more than one active view at the same time, then you might benefit from the possibility of passing a parameter from the view to the command in the view model. For example, in the DelegateCommand used to deactivate the view, you could accept a parameter that could be used to recognize the correct view that needs to be removed from the region or the view itself.

You can find more information about this in the following chapter of the Prism documentation:

I hope you find this useful,

Damian Cherubini
http://blogs.southworks.net/dcherubini

Jan 10, 2012 at 8:36 AM

Yes sorry didn't explain that very clearly, and probably still won't... I am using a content control, and then just navigate between patient views, keeping them alive unless they are closed.

 

The close command is not coming from the view itself is another slight problem, I'm not sure what parameter to identify the view I would send?

Developer
Jan 10, 2012 at 6:36 PM

Hi,

Based on my understanding, when using ContentControl as a region, the ContentControlRegionAdapter will adapt the region as a SingleActiveRegion by default. This means that it will only have one active view at a time (the one that is being shown) and all of its other views will be deactivated. Also, when navigating to a view, this view will be the active one and all of the other views in the region will be deactivated.

If I understood your scenario correctly, you want to remove the current view that the user is seeing in the aforementioned ContentControl. If that is the case, that view will be the active one, and you could retrieve it and remove it, doing something like this:

IRegion region = regionManager.Regions["MainRegion"];
region.Remove(region.ActiveViews.FirstOrDefault());

For example, if your region has three views (patient1, patient2, patient3) and the region is currently showing the patient2 view, invoking the FirstOrDefault method (which is an extension method provided by LinQ) on the ActiveViews collection of the region, should return the patient2 view, as patient1 and patient3 are deactivated.

Please let us know if we have misunderstood your scenario.

I hope you find this useful,

Damian Cherubini
http://blogs.southworks.net/dcherubini

Jan 11, 2012 at 9:33 AM

Yes thank you for clearing that up, that makes sense.

So if the ViewModel IsActive is false, I will just change the KeepAlive flag to false and unsubscribe from events. If it IsActive then I will also deactivate as you have shown.

Thanks again.

Jan 11, 2012 at 9:45 AM

So now the issue just seems to be with the one that isn't active. I presume it isn't deactivated properly because I have the KeepAlive set to true, is there something I need to do to force refresh this deactivation? What happens at the moment is I have two views active, I search for James and it opens up and becomes active, I then search for James1 and it becomes active. So then when I try to close James which is inactive, it isn't properly deactivated, because if I search for James again it navigates to the ViewModel that is already there. If I then navigate away from it back to James1, it is then deactivated and removed.

 

Hope that makes sense...

Developer
Jan 11, 2012 at 7:30 PM

Hi,

Usually, when a view is deactivated, this means that the view will be not visible in the region, but it will be still "stored" in the region without being modified. Based on my understanding, other behaviors like removing a deactivated view are not performed by default.

If I understood your scenario correctly, you are expecting that, after performing a navigation request, the previous view (which will be deactivated) is removed from the region instead of just being hidden. As far as I know, when a navigation request is performed, if the previous view or its view model implements the IRegionMemberLifetime interface and the KeepAlive property of the view return false, the view will be automatically removed from the region. However, if the KeepAlive property of the view or its view model return true, then the view will be deactivated (that is, the view will not be visible in the region) but the view will not be removed from the region by default.

You can find more information about the IRegionMemberLifetime interface in the following link:

If for some reason you need to explicitly remove or deactivate the previous view when performing navigation, as a possible approach you can could implement the INavigationAware interface in your view, and then, in the OnNavigatedFrom method, you could make the view remove or deactivate itself from the region.

You can find more information about this in the following chapter of the Prism documentation:

I hope you find this useful,

Damian Cherubini
http://blogs.southworks.net/dcherubini

Jan 12, 2012 at 5:01 PM

Apologies I have not been very clear because I think I have mislead you.

Basically I am using my single contentcontrol (SelectedPatientViewRegion) combined with a side panel that lists all of the patients that are KeptAlive as an alternative to a tabcontrol. My Side Panel has each patient view which still exists with a close button next to it, much like tab headers.

So if I click close in my side panel on a patientview that is not the selectedpatientview, how do remove it completely?

By reading your last comment I would have to remove the view myself, as this has been bypassed in the deactivation because its keepalive flag would have been true. Simply changing the KeepAlive flag is not enough it then needs to be removed.

At the moment my CloseCommand will run the following Method:

 

void Close(string patient)       

{           

if (patient == this.PatientID)           

{               

KeepAlive = false;               

_eventAggregator.GetEvent<PatientAddAppEvent>().Unsubscribe(AddApp);               

_eventAggregator.GetEvent<PatientOpenAppEvent>().Unsubscribe(OpenApp);               

_eventAggregator.GetEvent<PatientClosedEvent>().Unsubscribe(Close);


if (IsActive)               

{                   

IRegion region = _regionManager.Regions[RegionNames.SelectedPatientRegion];                   

region.Deactivate(region.ActiveViews.FirstOrDefault());               

}

else //Is not the selected view and needs to be removed.

{

//Need to remove View

//But like the original problem I don't know how to get reference to this view

}

}       

}

Jan 12, 2012 at 5:06 PM

So to summarise I am not navigating here I am just wanting to remove views completely. If I click close on the selectedview that works fine.

If after clicking close I search for the patient I just closed, it should create a new view.

Developer
Jan 12, 2012 at 7:24 PM

Hi,

Thanks for providing us with more information, I believe I have a better understanding of your scenario now.

As a possible approach for this case, I believe that you could create a Command which accepts a parameter used to identify the patient (similar to the Close method you posted above). This command could be, for example, in the view model of the side panel that contains the list of patients that are being kept alive. Then, supposing that each view has a view model with the corresponding patient identifier, you could iterate through the Views collection of the region, retrieving the view model and comparing the patient passed as a parameter with the patient of the view model. For example:

void Close(int patient)
{
    var viewToClose = null;
    IRegion region = _regionManager.Regions[RegionNames.SelectedPatientRegion];

    foreach(var view in region.Views)
    {
        /* For the purpose of this example we will assume that the view models 
         * for the patient views implement this interface or something similar.
         */
        IPatientViewModel viewModel = view.DataContext as IPatientViewModel;
         if( viewModel != null && viewModel.PatientId == patient )
         {
             // We found the view we want to close.
             viewToClose = view;
         }
    }

    if( viewToClose != null )
    {
        // ... logic to remove or deactivate the view ...
    }
}

I hope you find this useful,

Damian Cherubini
http://blogs.southworks.net/dcherubini

Jan 16, 2012 at 10:01 AM

Yes very useful thanks a lot for your help!