Prism modularity reload mechanism - silverlight

I'm trying to implement modularity and have some complications. I implemented one module which called ModuleA which shows new childWindow in its initialize function;
public ModuleA()
{
personViewModel = new PersonViewModel();
detail = new ViewDetail(personViewModel);
}
public void Initialize()
{
detail.Show();
}
My problem is that i can't show the view again because of missing opportunity of reload function. My module loaded on demand, i mean that i want to load module when user clicks button so, i do not have a chance to load module at the beginning and control its functions from its own events. then i tried to show view from my application like that;
private void ButtonModelA_Click(object sender, RoutedEventArgs e)
{
this.moduleManager.LoadModule(MyBootstrapper.ModuleAName);
ChildWindow detail = new ModuleA.ViewDetail(new ModuleA.ViewModel.PersonViewModel());
detail.Show();
}
in this way, loading module became unnecessary.
Is there a way to load module from out of it as on demand and show its view multiple times ?

I'm not quite sure how this works in Silverlight, but I think there is a misunderstanding of Prism.
Prism is based on regions. That means that the applications user interface consists of ContentControls (or other region capable controls) that state to be a region. The region manager now adds all views that want to reside inside a specific region into exactly this region.
The modules just have to tell the region manager inside which region the views implemented in the specific module wants to reside:
RegionManager.RegisterViewWithRegion( "RegionName", typeof( View ) );
If the specific region is currently not a part of the user interface, because the view that contains the control that hosts the region isn't part of the user interface itself, the view that wants to resied inside the region cannot be placed inside this region. The region manager just doesn't know of the region. To have the view shown you have to add the control that hosts the region to the user interface by hand.
Another way is to add a specific into a region by hand. Using this approach you don't have to register the view to the region manager. So when the region manager discovers the region it stays empty. Now you can add the view manually into the region using the region manager:
IRegion region = RegionManager.Regions["RegionName"];
region.Add( new View(), "ViewName" );
If you want to place views into a region depending on any state or user action you have to add the into the region by hand. Have a look at the Stock Trader Reference Inplementation. It explains in a very simple manner how to add views to regions triggered by user action.

Related

Re-navigate to a particular view in multi-view region

I'm working on a WPF application that's utilizing the Microsoft Prism framework. One aspect of the application is a "modal" region that can hold any number of modal windows that overlays over the entire window. As more views are navigated into the region, each window slides to the right to allow the new window to occupy the center of the screen. Here's a more visual explanation:
When the "modal" region contains a single view:
When another view is added to the region:
When several more views are added:
I have this working using a custom control that manages the animation and display of its children. Here's what the control's custom RegionAdapter's Adapt method looks like:
protected override void Adapt(IRegion region, ModalContainer regionTarget)
{
region.ActiveViews.CollectionChanged += new System.Collections.Specialized.NotifyCollectionChangedEventHandler((o, e) =>
{
if (e.Action == System.Collections.Specialized.NotifyCollectionChangedAction.Add)
{
foreach(FrameworkElement element in e.NewItems)
{
regionTarget.AddChild(element);
}
}
else if (e.Action == System.Collections.Specialized.NotifyCollectionChangedAction.Remove)
{
foreach (FrameworkElement element in e.OldItems)
{
regionTarget.RemoveChild(element);
}
}
});
}
My question is this: What's the best way to navigate back to an earlier window? Right now, the only way I know to trigger the RemoveChild method above is to explicitly remove the view from the region, which requires that I keep a list of all the views currently in the region somewhere:
// to remove the most recently added view from the region
_regionManager.Regions["ModalRegion"].Remove(addedViews.Pop());
Ideally, I would be able to navigate backwards using Prism's "journaling" concept, but I don't see a way in my RegionAdapter to respond to when a view already in the region is re-navigated to.
Any hints would be much appreciated.
EDIT
I was able to achieve this functionality by following a suggestion by GOstrowsky (see the comments in the accepted answer) - I changed my region adapter to only maintain a single active view in the region (the view currently in the center of the screen). I then can target that view for removal via myRegion.ActiveViews.FirstOrDefault().
YET ANOTHER EDIT
I have since changed this implementation yet again, as we needed the ability to remove any of the views currently in the region, not just the last one. See the accepted answer for details.
If you would ocassionally want to Navigate back to a previous View, you should not remove it from the Region when Navigating from it.
Instead, you could just deactivate it from the OnNavigatedFrom() method. And then, use the NavigationJournal to navigate back.
Regarding your RegionAdapter control you could modify it so that it could handle View activation and de-activation. For example, you could Publish a De/ActivationChanged event from each ViewModel OnNavigatedFrom() and OnNavigatedTo(), and handle these events on your custom control by Subscribing to it and performing the corresponding task to each event.
You can find more information about Navigation and Event Aggregation in the following MSDN Prism Guide chapters:
8: Navigation
Event Aggregation
I hope this helps,
Regards.
Initially, I solved this problem by only allowing a single region to be active, which guaranteed that removing the region's currently active view always removed the view currently in the center of the screen. However, since then, we've needed the ability to remove any of the views from the region, not just the first. To accomplish this, I realized that the Region.Views property can be cast to a List and then accessed by index:
List<object> allViews = modalRegion.Views.ToList<object>();
I'm a little uncomfortable with this solution, since the IViewsCollection definition inherits from IEnumerable, not IList; technically I could be handed a custom IViewsCollection that cannot be cast to an IList.... but in the short term I'm going to run with this.

How can i activate multiple views belonging to the same region?

I'm new to Silverlight and Prism. I'm developing a dashboard application.
Here's my question.
I've 2 regions: 1. Login 2. Maincontainer.
My Login region has one view and my maincontainer has 2 views.
After a successful login, i need to deactivate the Login region or view (whichever is simple) and then i need to activate the Maincontainer region and both the view within it.
Is there a way i can activate both the view of my "Maincontainer" region at the same time?
Any help would be useful.
Thanks in advance
Samit
To activate a view in a region means to display it. If you want a region which can display more than one view at the same time, you must use an AllActiveRegion. To use regions of this kind you must use as a region host an ItemsControl (or any other that inherits from it).
You can find some more info here.
I hope this helps.

Cannot find Region in RegionManager (using PRISM)

I'm writing an application in Prism. I have a user control and contains two <ContentControl> controls. These both have Regions assigned to them. The usercontrol is being hosted in a Window that is being shown using ShowDialog().
I'm adding the one of my views to a region using view discovery and the other I want to inject the view into its region. The view discovery works fine. However when I try and reference the other region to inject the view I get the exception:
KeyNotFoundException
The region manager does not contain the MyRegion region.
When I look in the RegionManagers regions neither the one that I'm trying to inject the view exists or the one being that's using view discovery.
Does it matter that I'm in a different window to the Shell? I thought there was only one RegionManager, but there must be two for my view discovery to still be working...? Or is it because I have two new regions being created later in the applications life cycle? Or is it because the new regions aren't inside the my MainRegion?
EDIT:
After doing some digging it looks like the Region is created but it can't find an instance of the RegionManager so it doesn't get added. Any clues?
Sorted now. I needed to register the region manager in the constructor of my presenter class.
That way the regions in my new window could find my global region manager.
RegionManager.SetRegionManager(view as DependencyObject, regionManager);

WPF Prism - To use Scoped Regions or not?

I have a WPF project based upon Prism Feb 2009 release set up as:
Shell exposes a single ContentControl as "MainRegion"
Another view (user control) defined in the Infrastructure project called SplitView exposes two additional regions "LeftRegion" and "RightRegion" also as ContentControl.
Some of my application's modules need to display their view in the MainRegion (one user control), while others need to display their views (two user controls in a split fashion) in the LeftRegion and RightRegion.
I have tried using scoped regions, assuming that specific Controllers would hold references to the scoped regions. So basically each controller interested in SplitView functionality should instantiate a new SplitView (user control) and activate it in the MainRegion while activating its two user controls in the LeftRegion and RightRegion of the newly created scoped SplitView regions.
I am using MVVM with View Injection to display the views.
Needless to say, something has gone horrifically wrong with this approach.
At runtime I get this exception, "An exception occurred while creating a region with name 'LeftRegion'. The exception was: System.InvalidOperationException: Specified element is already the logical child of another element. Disconnect it first."
Am I correct at assuming that the LeftRegion and RightRegion are trying to register themselves with the main RegionManager every time I instantiate the SplitView?
Sorry about the confusing/verbose post. Any suggestions? Best practices to achieve this?
Thanks in advance,
Ali
The exception of "Specified element is already the logical child..." is what happens when you try to add something to two places in the tree, so I imagine there might be some logical error in your code, or you are adding something twice.
I generally create my sub regions like this:
m_scopedRegionName = Guid.NewGuid().ToString(); /* EXAMPLE ! */
m_scopedRegionManager = m_regionManager.Regions[RegionNames.WORKSPACE_REGION].Add(myViewModel.View, m_scopedRegionName, true);
m_someThingRegion = m_scopedRegionManager.Regions[RegionNames.SOME_THING_REGION];
Then I add any new stuff into the "m_someThingRegion".

Removing View from Region in Silverlight PRISM

I have a Region which can have only one active view at a time. I want to add new view to the region on user action and remove the existing view from the same region. I also want to maintain the cache of few views. If the no of view is more than the specified limit then I will remove the oldest view. Is there any direct support for it or I have to implement the Region adapter for it. Is there any other better approach for the same?
Well, let me answer your two questions.
First, if you want a Region to only show one view (like say you have a region defined as a ContentControl), that is possible. You can add many views to that region and only the active one will be shown. To show a different view in that region that has already been added, you would simply Activate that view:
var region = regionManager.Regions["TabRegion"];
region.Add(view1);
region.Add(view2);
region.Activate(view2);
In this way, you can have many instantiated views ready to go, but only one visible.
Second, with the expirations. I'd say a region adapter would be the cleanest and most correct way, but you could just create an expiring cache for these and when they expire, you can remove them from the region if they aren't active:
var region = regionManager.Regions["TabRegion"];
region.Add(view1);
regionTracker.Add(view1, region, TimeSpan.FromMinutes(10));
region.Add(view2);
regionTracker.Add(view2, region, TimeSpan.FromMinutes(10));
region.Activate(view2);
And then the implementation of your expiration for your regionTracker could just:
if(!region.ActiveViews.Contains(ViewThatJustExpired))
{
region.Remove(ViewThatJustExpired);
}
It's a bit half-baked, but hopefully this will give you some idea of where to go.
Take a look at my blog post about dynamic module loading in PRISM with Navigation. In that post you'll see how I use a multiple-view container, then swap views into and out of focus. It involves having an interface for navigation, then raising events that swap the view state using the visual state manager.
Click Here to View
Jeremy

Resources