Removing View from Region in Silverlight PRISM - silverlight

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

Related

How to add a region without knowing the type and without activating it

RequestNavigate(uri) is nice when you want to navigate to region using a string and immediately make it the active view. But what if you want to add a region without activating it? Is there a way to do that with a string?
I have a view model that needs to add some views to a docking control dynamically. These views should not be activated when they are added. Adding a region using Region.Add works but I have to give it a type:
RegionManager.Regions[KnownRegionNames.DockingRegion].Add(typeof(MyView));
I feel like this violates some MVVM principals of making the ViewModel completely independent from views. It's not absolutely terrible since I can probably mock out the region manager in testing, but is there another way?
You'll have to somehow identify the view you want to add to your region, either by type or by string.
You can use the string to resolve it from the container (basically what the region manager does) and add the resolved view to the region.

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.

Should I use a central event bus with backbone.js?

I am currently working on my first backbone.js app. The concept of events is quite familiar to me but I am in question if I should use a central event dispatcher or not. Generally I see these two approaches:
Directly wire event publishers and event receivers together. (I started with this approach.)
Use an event bus and wire publishers and receivers to this bus.
Is it favorable to use an event bus in terms of e. g. long time maintainability of the app and traceability of events?
Whether to use a central event bus or to directly wire events together should be assessed on a case by case basis. Depending on the circumstance, there are times where you will favour one over the other.
I will use a central event bus whenever I can due to the fact that the publisher and receiver are not tightly coupled. In my opinion, this makes your code more maintainable and flexible.
I find central event buses very useful when you have:
A single event publishing instance and numerous event receivers.
Many event publishing instances and a single event receiver
The benefits of the central event bus in the above cases becomes even more apparent when the event receiver instances or publisher instances are dynamic and thus being created and destroyed over the life of the Application. As an example, consider the following single page app:
The single page app must stretch to fit the browser window width.
The single page app is primarily a tabbed set of views, however the number of tabs is dynamic as they are created and destroyed by users
The contents of the tabs are all different with the exception that they have a main area of content that must be stretched to fit the available width after accounting for the width of other sibling elements
Resizing needs to occur at many different points due to the contents of tabs continually changing.
In the above case, regardless of the specific scenario, the central bus model works really well. A code example would be:
Application Level
var App = {
init: function () {
// Event to resize width of element to remaining width
App.Events.on("fitRemainingWidth:app", this.fitRemainingWidth);
},
// event pub sub for the app.
Events: _.extend({}, Backbone.Events),
// function that expands an element, or elements to the remaining width in the window.
fitRemainingWidth: function(targetEls) {
var els = $(targetEls);
_.each(els, function (e) {
var el = $(e);
var cont = el.parent();
newWidth = cont.innerWidth();
otherElements = cont.children().not(el);
otherElementsWidth = 0;
otherElements.each(function () {
otherElementsWidth += $(this).outerWidth();
});
el.width(newWidth - otherElementsWidth);
});
}
}
In your views
Whenever a something needs to be resized, you trigger the event, for example:
App.Events.trigger("fitRemainingWidth:app", this.$(".main-content"), this);
As you can see, this is really useful, because the fitRemainingWidth function can apply to anything at all and you never know which views may want to use it in the future or when it will need to be called.
So I guess I should move onto when I find not using a central event bus preferable. I am sure there are other cases, however the main one for me is when the receiver needs to be wired to a specific instance of the publisher. For example, if we continue on with the tabbed application example, lets say that the contents of each tab is a split view of a particular person's mailbox. In each split view is a list pane showing a collection of emails and a reading pane showing the content of the currently selected message in the list.
In this case we want to trigger a "selected:email" event when an email is clicked and have the appropriate reading pane bound to it so it can display the email's contents.
Say we have ten mailbox tabs open, each with their own email list pane and reading pane. That makes ten email lists and ten reading panes. If I was to use a central event bus for this scenario when I triggered a "selected:email" from one of the lists, the reading panes that are receiving these events from the event bus would need to have their own smarts to try and work out whether the selected email was something they need to display or not. It's not really desirable for each reading pane to try and work this out and there is uneccesary logic involved. Its far better for a reading pane to just receive a "selected:email" event with the email object as it's payload and just display it.
So in this scenario, its better to to make each mailbox tab view responsible for wiring the events of it's specific child view instances and their models and collections. In our example this means a specific instance of an email list view with it's collection and a specific associated instance of a reading pane view.
In summary, I am suggesting there are use cases for each and that you should try to be judicious on choosing the right approach for each circumstance that you come across. Use both and learn to see what fits in which circumstances.
Lastly, in regards to the traceability of events, you can always write a wrapper for the On and Off functions in your event bus that both calls the normal On and Off functions but also adds/removes information to a register containing which objects are bound to which events. You could use this for debugging and write to the console information about these objects so you can have a clear picture of the state of your event bus and its listeners at any point in time. I've not ever thought about doing this, but there is no reason why you couldn't ;)
Post Edit: The comment by tsiki regarding using Local Event buses is a good one. In the above example I made with many tabbed mailboxes, you could use a local event bus for each user mailbox tab view. You might want to do this if each mailbox tab view became quite complicated in its own right containing many nested child views and large amounts of events to be generated/handled.

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.

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".

Resources