IRegion GetView() doesn't work

Topics: Prism v4 - Silverlight 4
Mar 28, 2011 at 7:06 PM

Hi,

It seems I have discovered a bug, what do you think of this?

 

 

var contactViewModel = _container.Resolve<ContactViewModel>();
contactViewModel.Initialize(contact);
var view = new ContactView(contactViewModel);
view.Name = contact.ContactID.ToString();
_regionManager.Regions[Constants.MainRegion].Add(view);
var test = _regionManager.Regions[Constants.MainRegion].GetView(contact.ContactID.ToString());

 

 

test should clearly give me the View, which was just added, but it returns null. However I can easily find the Views inside

_regionManager.Regions[Constants.MainRegion]

The method doesn't seem to work correctly. Can anyone confirm this please?

Many Thanks,
Houman 
Developer
Mar 29, 2011 at 2:50 PM

Hi Houman,

The way to add a named view to a region is to use the IRegion.Add method passing both the view and the name for it. Prism's UI Composition mechanisms are not designed to detect the FrameworkElement.Name property (possible due to the fact that any kind of object, such as a string, can be added to a region as a view).

You could try modifying your code like this:

var contactViewModel = _container.Resolve<ContactViewModel>();
contactViewModel.Initialize(contact);
var view = new ContactView(contactViewModel);
string viewName = contact.ContactID.ToString()
_regionManager.Regions[Constants.MainRegion].Add(view, viewName);
var test = _regionManager.Regions[Constants.MainRegion].GetView(contact.ContactID.ToString());

I hope you find this helpful.

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

Mar 29, 2011 at 2:59 PM

Oh that makes sense.

Thanks Guido, as usual you are the savour :)

May 12, 2011 at 12:18 AM
Edited May 12, 2011 at 4:19 AM

@GuidoMaliandi, I am having the same scenario where IRegion.GetView returns null just like Houmie explained.

But in my scenario I am using RequesNavigate, and I don't provide the View at all (parts ordered by execution plan):

//....... Bootstrapper:
protected override void ConfigureContainer()
{
  base.ConfigureContainer();
 
  Container.RegisterType<objectLoginView>(typeof(LoginView).Name);
}
//...... In another place LoginManager.cs
IRegionCollection regions = regionManager.Regions;
Action navigate = () => regionManager.RequestNavigate(RegionNames.MainContentViewNames.LoginView);
 
if (regions.ContainsRegionWithName(RegionNames.MainContent))
  navigate();
else
{
  regions.CollectionChanged += (sendere) =>
  {
    if (e.Action == NotifyCollectionChangedAction.Add && regions.ContainsRegionWithName(RegionNames.MainContent))
      navigate();
  };
}
//....... And finally, after some event is raised (LoginManager.cs)
var region = regionManager.Regions[RegionNames.MainContent];
//Doesn't work - returns null
var view = region.GetView(ViewNames.LoginView);

//Works fine - returns the LoginView instance.
if (view == nullview = region.Views.OfType<LoginView>().SingleOrDefault();
//Another way that will not constraint the use of LoginView type.
if (view == nullview = region.Views.SingleOrDefault(v => v != null && v.GetType().Name == ViewNames.LoginView);
//Remove the view
region.Remove(view);
May 12, 2011 at 6:11 AM

Hi @weitzhandler,

When you do requestnavigate it does not add a named view to the region. So GetView by name wont work.

May 12, 2011 at 6:23 AM

Any better way than the ways I did to achieve it?

Anyway to create an ex. method RequestNavigate that will add the View name to the region collection?

May 12, 2011 at 8:13 AM

I dont understand the bit where you have hooked up the regions collection changed. You should use a RegionAdapter in that case. If your region is a SingleActiveRegion then each to navigate to a view in the region it automatically removes the old views from its Views collection. You dont have to explicitly call remove on the region manager.

May 12, 2011 at 5:02 PM
Edited May 12, 2011 at 7:18 PM

@gan_s, I will check out about RegionAdapter, thanks!

If you can post an example, that would be great as well.

May 12, 2011 at 7:58 PM
gan_s wrote:

You should use a RegionAdapter in that case. If your region is a SingleActiveRegion then each to navigate to a view in the region it automatically removes the old views from its Views collection. You dont have to explicitly call remove on the region manager.

How do I work with the RegionAdapter? how do I get the adapter with IoC (Unity)? Can you please provide an example or any other resource?

May 13, 2011 at 7:45 AM
Edited May 13, 2011 at 8:44 AM

The RegionAdapter basically tells the host how to manage views being loaded into it. Prism has region adapters built in for ItemsControl/ContentControl and TabControl. Lets take the example of ContentControl.

In the ContentControlRegionAdapter it creates a SingleActiveRegion and it handles the views in its content such that at anyone point in time there is only one view loaded in to the Content.

If you want to create a RegionAdapter for a Grid you would specify how you would want the views to be added to the Grid.Children. Thats the purpose ! So you dont have to worry about adding/removing views wherever you want to, instead it is taken care by the RegionAdapter. Custom region adapters need to be registered in the ConfigureRegionAdapterMappings in your Bootstrapper (MEF/Unity).

That aside, I have written an extension method that will help you do the GetView on the IRegion with a RequestNavigate. So you would simply use it like this

 

_regionManager.RequestNavigate(MainRegion, new Uri("Test.View1", UriKind.Relative), "MyView");

var view = _regionManager.Regions[MainRegion].GetView("MyView");

 

And the extension method is this (as of now only works with MEF. you can tweak it to work with unity):

 

 
public static class CustomRegionManagerExtensions
    {
        public static void RequestNavigate(this IRegionManager regionManager, string regionName, Uri source, string viewName)
        {
            var region = regionManager.Regions[regionName];
            regionManager.RequestNavigate(regionName, source);
            var viewExportName = source.OriginalString;
            var viewToAddKey = (from view in region.Views 
                                    let type = (ExportAttribute[]) view.GetType().GetCustomAttributes(typeof (ExportAttribute), false) 
                                   where type.Any(t => t.ContractName.Equals(viewExportName))
                                   select view).FirstOrDefault();

            if (viewToAddKey == null) return;
            
            try
            {
                if (region.Views.Any(v => v.Equals(viewToAddKey)))
                {
                    region.Remove(viewToAddKey); // This throws an ArgumentException and also removes the view
                }
            }
            catch (ArgumentException)
            {
            }
            finally
            {
                // This adds it to the region again with the view name you specify
                region.Add(viewToAddKey, viewName);
            }
        }
    }

 

Hope this helps.

Cheers!