Prism-MEF: using UriQuery to dynamically adding tabitems

Topics: Prism v4 - WPF 4
Jan 27, 2013 at 7:02 AM
Edited Jan 27, 2013 at 7:04 AM


In a previous thread "prism: problems with dynamically adding tabitems" i was lucky to learn from Agustin Adami and Damian Cherubini in my way to solve the problem with their help.
During my trials to solve the problem i tried to use diffrent approach for dynamically adding
TabItems.One solution i found while searching was by using UriQuery class. The beauity of this approach is that it removes the coupling with the view to be displayed and its View Model and it was like this:

private void ShowSubsystemDetails(PreCommissioning.Model.Subsystem Subsys)
   {
            var uriQuery = new UriQuery();
            if (Subsys != null)
            {
                uriQuery.Add("SubsystemNo", Subsys.SubsystemNo);
            }

            var uri = new Uri("SubsystemDetailsView" + uriQuery.ToString(), UriKind.Relative);

            regionManager.RequestNavigate("MainRegion", uri);
   }
this code included in controller class.
but i had this exception when trying to open the tabitem:
   "Activation error occured while trying to get instance of type Object, key "SubsystemDetailsView"
i hope that there is solution to such exception becuase i think this approach is better than view injection.

Developer
Jan 28, 2013 at 12:48 PM
Edited Jan 28, 2013 at 12:55 PM

Hi,

I'm glad you found the way to solve your previous problem.

Regarding this exception, based on my understanding this error could be caused if you are not exporting your views with the corresponding contract name.

Take into account that as mentioned in this section of the documentation when the region navigation service creates a view, it requests a type of Object from the container with a name that matches the one supplied in the navigation URI. In MEF, only the contract name is used. Therefore, as long as the view is exported with a contract name that matches the name in the URI request, the view can be successfully constructed.

For example you could try exporting you views like this:

[Export("SubsystemDetailsView")]
[PartCreationPolicy(CreationPolicy.NonShared)]
public partial class SubsystemDetailsView : UserControl

I hope this helps,

Agustin Adami
http://blogs.southworks.net/aadami

Jan 28, 2013 at 4:01 PM

Hi,

I'm happy when i see your name in a reply because i feel then that a new problem in its way to be solved.

As per our current problem it is resolved as i expected but the next problem is that i pass the Subsystem property to  SubsystemDetailsViewModel

using this statement

                   "subsysdetailsViewModel.Subsystem = Subsys;

how i can do this with UriQuery.

and if it is not possible what is the alternative?

Developer
Jan 28, 2013 at 5:10 PM

Hi there,

When using navigation requests the parameters you can pass in the UriQuery are limited to strings only; hence, its not possible to pass an entire object (unless you can serialize it in a string and recreate it based on it, although this not always recommended)

Based on my understanding, the usual approach in this kind of scenarios is to pass only an ID which you can use to identify the corresponding model (for example, the SubsystemNo parameter you are adding to the Uri) and then obtain that model in the OnNavigatedTo method by using that ID, which you can retrieve from the NavigationContext's Parameters:

public void OnNavigatedTo(NavigationContext navigationContext)
{
    string id = navigationContext.Parameters["SubsystemNo"];
    
    ...
}

How you would obtain the model based on this ID will depend mostly of the architecture of your application. For example, you could obtain the model from a repository or shared service that could be in charge of managing the models. Another approach could be to simply store the model in a shared service before requesting the navigation and then retrieve the model from that service in the OnNavigatedTo method.

I hope you find this useful,

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

Jan 28, 2013 at 6:17 PM

Hi,

Happy to see your replies again.

I think i have to implement INavigationAware : OnNavigatedTo, OnNavigatedFrom, IsNavigationTarget right?

also do i have to implement IConfirmNavigationRequest?

thanks in advance.

 

 

 

 

 

 

Developer
Jan 28, 2013 at 6:57 PM

Hi,

You are right. In order to follow the approach I mentioned above you will need to implement the INavigationAware interface in the view model of the corresponding SubsystemDetailsView. This will allow for the view model to "participate" in the navigation progress and receive the parameters passed in the navigation request.

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

Regarding the IConfirmNavigationRequest interface, I believe it's not required to implement it for this particular approach. This interface is used when you want to give a view model the ability to confirm or cancel a navigation request (for example, if there are unsaved changes in a view) when the view is "navigated from." The use of this interface will depend mostly if you need to take advantage of those capabilities or not.

You can find more information about the IConfirmNavigationRequest in the following section:

Regards,

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

Jan 28, 2013 at 7:06 PM

Hi,

Thanks Damian i will try to implement this approach and give feedback when i finish.

 

Jan 28, 2013 at 7:41 PM

Hi,

Thanks Damian, thanks Agustin

I implemented INavigationAware.OnNavigatedTo() and used Service to obtain the model.

Only one concern, with the old approach the View Model was not aware where the data comming from or where

it going to, which is a good characteristic of View Model, but now the View Model knows where the data is

comming from which is bad practice. Any suggestions? 

Developer
Jan 28, 2013 at 8:55 PM

Hi,

Based on my understanding consuming a service in your view model to retrieve the data that will be used in them, shouldn't be a bad practice. Take into account that you can make your view model be loosely coupled from the specific implementation of this service, for example by resolving the service through a common interface (these are commonly called shared services, which are explained in this section of the documentation).

Also, an example of this can be seen in the StockTrader RI, particularly in the MarketFeedService.cs , which exposes the data obtained from parsing two XML files. This services is registered as singletons in the container trough a common interface (IMarketFeedService) so it can be consumed in different parts of the application. For example this service is injected through this interface in the WatchListViewModel.

Regards,

Agustin Adami
http://blogs.southworks.net/aadami