How to pass an Entity to Views?

Topics: Prism v4 - Silverlight 4
Feb 15, 2011 at 6:29 PM

Hi,

 

I have a ContactView that contains a TabControl.  By default there are always three tabs on this tab control. Each tab item would contain a different View.

<Grid x:Name="LayoutRoot" >
		<sdk:TabControl Name="ContactTabControl">
			<sdk:TabItem Header="Contact" Name="CustomerTabItem">
				<Grid>
					<Views:CustomerView/>
				</Grid>
			</sdk:TabItem>
			<sdk:TabItem Header="Calls" Name="Calls">
				<Grid>
					<Views:CallsView />
				</Grid>
			</sdk:TabItem>
			<sdk:TabItem Header="Info" Name="Info">
				<Grid>
					<Views:InfoView />
				</Grid>
			</sdk:TabItem>
		</sdk:TabControl>
	</Grid>

Nonetheless all three Views would represent a subset of the Contact Model. And here lies the challenge, the plan is to pass a contact from a collection into this viewmodel so it can be displayed.  
Now I could be passing the contact object to the ctor of the Contact Module by utilizing an event aggregator. 
But here I am stuck, generally I would apply the view first approach by creating my ContactView like this:

_regionManager.RequestNavigate(Constants.MainRegion, new Uri(Constants.ContactView, UriKind.Relative));

Or the classic way like this:
 

  _regionManager.RegisterViewWithRegion(Constants.MainRegion, () => _container.Resolve<ContactView>());


However how would I pass in there my Contact object if I am utilizing the IoC for resolving it in first place?  

But even if I got the contact object passed in, I still need to pass it down further to the three sub views in each tab as shown previously. As you might guess each of the three views has its own ViewModel which might need the contact to be passed in as well. How would I achieve this, especially since those three views are instantiated automatically as soon as the Contact View is created (in xaml), which doesn't leave me any room to inject in anything.

Many Thanks for your advice,
Houman 


Developer
Feb 15, 2011 at 6:59 PM

Hi Houman,

One possibility would be to create an UriQuery object, append it to the Uri used in the RequestNavigate method and pass the information you need from your Contact object, which could be retrieved in the Parameters property of the Region Navigation Context in the OnNavigatedTo method. For example:

Contact contact= Contacts.CurrentItem as Contact;
if (contact != null)
{
UriQuery query = new UriQuery();
query.Add("ID", contact.Id);
_regionManager.RequestNavigate(RegionNames.TabRegion,
new Uri("ContactView" + query.ToString(), UriKind.Relative));
}

Since passing an object as a parameter for a navigation request isn't supported out of the box (as you can read in this thread), in case you need to pass the full object, you could use other mechanism for constructing the view to be navigated to. For example, one possibility would be not to use Inversion of Control to obtain a reference to the ContactView, and pass the entire Contact object to the view's constructor instead.

I hope you find this helpful.

Guido Leandro Maliandi
http://blogs.southworks.net/gmaliandi

Feb 15, 2011 at 11:03 PM

Hi Guido,

 

Many Thanks for your help.  The idea of passing in the object into View/VM ctor directly and not utilizing the IoC is possible.

However I am facing a side effect.

In the example above, as you see my three sub views are created in XAML (with empty ctor) and therefore blendable. I can see the Designer.  However if i was to inject the Contact object first into the parent View, which needs then to pass the object further down to all three child views, I have a problem.
The only way I know how to do it, is to remove the view entries from XAML and add them in the code behind directly with the Contact object injected into their ctor.  However the Blendability is gone, I wont be able to see my views in the Designer. 

ParentView:
 
<sdk:TabItem Header="Contact" Name="CustomerTabItem">
				<Grid Name="GridCustomer" Background="{StaticResource BlueGradientBackgroundColor}">

		                </Grid>
			</sdk:TabItem>


   Code behind of ParentView:
GridCustomer.Children.Add(new CustomerView(contact));


 

I was wondering if there is a better way of doing it.  Perhaps instead of the View-First approach, I should be using the ViewModel first approach.  Do you have any example how to do this? And is this approach blendable? (Probably not either I take it, but might still be cleaner)

Many Thanks,
Houman

Developer
Feb 16, 2011 at 4:36 PM
Edited Feb 16, 2011 at 4:37 PM

Hi Houman,

As explained in this thread, the ViewModel-First approach could bring some difficulties to blendability. From Brian Noyes' answer in the aforementioned thread:

"The real downside to the view model first approach that the docs should be pointing out is a lack of Blendability - the view doesn't know about the properties exposed by its data context, so you can't leverage the VS and Blend design time data tools as well. Additionally, it locks a view model into supporting only a single view contract, and it is not infrequent that a single view model may be able to support more than one view type (i.e. a summary and a details view). It is also one more dependency that needs to be mocked out when testing the view model, whereas you usually don't write unit tests against the view so there is not the same concern of substitutability when the view has a ref to the view model as there is when the roles are reversed."

You might find the following threads useful to achieve blendability in Prism:

I hope you find this helpful.

Guido Leandro Maliandi
http://blogs.southworks.net/gmaliandi

Feb 22, 2011 at 5:07 AM

Hi Houman,

I think you could use the RegionContext to share objects between the views in the region.

Cheers