Prism 4: Unloading view from Region? - prism

How do I unload a view from a Prism Region?
I am writing a WPF Prism app with a Ribbon control in the Shell. The Ribbon's Home tab contains a region, RibbonHomeTabRegion, into which one of my modules (call it ModuleA) loads a RibbonGroup. That works fine.
When the user navigates away from ModuleA, the RibbonGroup needs to be unloaded from the RibbonHomeTabRegion. I am not replacing the RibbonGroup with another view--the region should be empty.
EDIT: I have rewritten this part of the question:
When I try to remove the view, I get an error message that "The region does not contain the specified view." So, I wrote the following code to delete whatever view is in the region:
// Get the regions views
var regionManager = ServiceLocator.Current.GetInstance<IRegionManager>();
var ribbonHomeTabRegion = regionManager.Regions["RibbonHomeTabRegion"];
var views = ribbonHomeTabRegion.Views;
// Unload the views
foreach (var view in views)
{
ribbonHomeTabRegion.Remove(view);
}
I am still getting the same error, which tells me there is something pretty basic that I am doing incorrectly.
Can anyone point me in the right direction? Thanks for your help.

I found my answer, although I can't say I fully understand it. I had used IRegionManager.RequestNavigate() to inject the RibbonGroup into the Ribbon's Home tab, like this:
// Load RibbonGroup into Navigator pane
var noteListNavigator = new Uri("NoteListRibbonGroup", UriKind.Relative);
regionManager.RequestNavigate("RibbonHomeTabRegion", noteListNavigator);
I changed the code to inject the view by Registering it with the region, like this:
// Load Ribbon Group into Home tab
regionManager.RegisterViewWithRegion("RibbonHomeTabRegion", typeof(NoteListRibbonGroup));
Now I can remove the RibbonGroup using this code:
if(ribbonHomeTabRegion.Views.Contains(this))
{
ribbonHomeTabRegion.Remove(this);
}
So, how you inject the view apparently matters. If you want to be able to remove the view, inject by registration with the Region Manager

StockTraderRI Example Project by Microsoft contains the following example of removing views from region in ViewModel.
private void RemoveOrdersView()
{
IRegion region = this._regionManager.Regions[RegionNames.ActionRegion];
object ordersView = region.GetView("OrdersView");
if (ordersView != null)
{
region.Remove(ordersView);
}
}

Is it possible you have a RegionAdapter that is wrapping the view inside another view before adding it? The ribbonHomeTabRegion should have a property with the collection of views - is there anything inside it?

Related

Correct use of Prism navigation service in xamarin forms

I want to confirm if I am using the prism navigation service with xamarin forms correctly. I have a master detail page, a styled navigation page and a bunch of content pages.
right now I am using the service in the following way:
var prj = await dataService.GetLwdProject(appState.SelectedProjectId);
var nparam = new NavigationParameters();
nparam.Add("Project", prj);
await NavigateTo("RootPage/StyledNavigationPage/SessionsListPage", nparam);
Where the Master detail page is the RootPage object. So expecting that when a user selects an item from this list page the correct way to the service should be:
var nparma = new NavigationParameters();
nparma.Add("Session", option);
await App.NavigateTo("RootPage/StyledNavigationPage/SessionsListPage?ProjectId=" + option.ProjectId + "/LocationListPage", nparma);
What I expect that just a LocationListPage would be added to the navigation stack, but when I use the the hardware back button on android it looks like that not only the last page was added but the whole path (all pages). So is this the correct way auto construct the desired path?
No. Navigation is always relative to where you are calling it. What you have now will navigate to the entire deep link you have created every time. Just navigate to your target NavigationPage/SessionListPage and pass your parameter. Though, you won't get a new page every time in this case, since you are navigating to the same view, but just passing different state.

Load a Xaml inside a stacklayout in Xamarin forms

I have a Content View in Xamarin forms with 2 StackLayout horizontally aligned in it. I want to change the Content of the 2nd Stacklayout dynamically, but I don't want to use Master details page in that case. Please find an attachment to see what my UI looks like. I want to load different pages in StackLayout 2 on button clicks in StackLayout 1.
Update: I want to achieve above using MVVM.
You specifically said you want to load different pages inside Stacklayout2.
Out of the box, this is not possible. A Page can not be nested inside another view in Xamarin.Forms*
However, you most likely don't need to nest a whole page either, which is good news.
You can create custom xaml views as separate xaml files, and then reference them like regular controls. For example you would create a xaml file MyDataView, inside you could use a and fill it out with your different labels, entries and what nots, then instantiate and add that MyDataView inside your page like you would any other control.
For your host page, I would recommend you change your StackLayout2 to a ContentView, as it will only ever contain one view, which in case of something like your custom "MyDataView" from above, would actually contain the stack layout and all the details.
From the point of view of your page, it has the left layout with all the buttons, and it has the "container" on the right to host different complex views. So it does not need a stacklayout there.
There is also an important decision that you need to make on when and how you want to instantiate all the views that you will host inside the right pane.
You might choose to instantiate them all at once, when loading the page, if there aren't too many. Then switchig to each one during page use should be quite quick. Something like this:
public partial class MainPage
{
private MyDataView myDataView = new myDataView();
private OtherView otherView = new OtherView();
private ThirdView thirdView = new ThirdView();
public void OnSomeButtonClick(object sender, EventArgs e)
{
Container.View = myDataView; //Container would be the x:Name you gave to your ContentView control on the right side of the page that will host the different views
}
}
Or you might prefer to "lazy" instantiate the views, which would only instantiate the view the first time it is navigated to. This is useful if some views will never actually be accessed, and you can save some cpu cycles and ram by not loading the view until it's needed. The downside of course is when you do load it for the first time, it will load slower. Like this:
public partial class MainPage
{
private MyDataView myDataView;
private OtherView otherView;
private ThirdView thirdView;
public void OnSomeButtonClick(object sender, EventArgs e)
{
if (myDataView == null) myDataView = new MyDataView();
Container.View = myDataView; //Container would be the x:Name you gave to your ContentView control on the right side of the page that will host the different views
}
}
And finally you can instantiate the view everytime it is needed. I would not go with this route in most cases. Unless you really specifically need to recreate the entire view each time (like if you are dynamically changing it during use, and you need to reset it when it's shown again)
*I have seen a custom renderer implementation that nested a whole page inside a view on github. I did not test it as it was not completely implemented at the time.
Cant you do something like this:
button1.OnClick += (sender, args) =>{
StackLayout2.Children.Remove(currentview);
StackLayout2.Children.Add(newview);
}
Not sure if its called onclick or clicked.

EmberJS: programmatically adding a child view to a collection view without changing underlying content

My EmberJS app is confusing me a lot at the moment. I have a collection view, that in turn defines an itemViewClass of a custom view I have defined in my code. Something like:
App.CarouselView = Ember.CollectionView.extend({
itemViewClass: App.SlideView.extend(),
});
And this CarouselView is rendered inside a template that has a dynamic segment backing it (I hope this makes sense?) . The controller for these dynamic segment is an array controller because the model for these dynamic segments is a collection :) (more confusion, please let me know)
App.SlidesController = Ember.ArrayController.extend();
By now all of you have figured that I am basically rendering a bunch of slides inside of a carousel. And these are dynamically backed in the collectionView by setting the property
contentBinding:'controller' // property set in CarouselView, controller corresponds to SlidesController
The confusion begins now. I want to add a slide to the existing set of slides. So I provide a button with an action : 'add' target='view'
In the SlidesView,
actions:{
add: function(){
var carouselView = this.get('childViews')[0];
// SlidesView has carouselView and another view as it's child, hence this.get('childViews')[0] is carouselView
var newCard = carouselView.createChildView(App.SlideView.extend());
carouselView.get('childViews').pushObject(newCard);
}
}
The above piece of code sucks and hurts me bad. I want to basically add a new SlideView to my CarouselView collection programmatically upon a button trigger. But apparently Ember recommends that childViews should not be manipulated directly and I ought to instead change the underlying content.
It states in my console.log that manipulating childViews is deprecated etc.
So essentially I need to add something to my content to my SlidesController content ? However, I don't want to add something to the content, this is just a soft add, that is providing the user with a slide so that he may choose to edit or add something if he wants to. He can always discard it. A hard add that will require me to persist the new slide to the DB will come once the user decides to save this information.

How to change the views in Prism?

just started stydying Prism. Can someone explain what I am doing wrong here...
I have basically 2 regions. 1 region - left menu. 2 region - content. They are all in the shell.
Left menu view and the content views are all in the separate projects.
Now, I would like on some button clicks in the left menu switch the views in the content region.
What i have done:
- created NavigationCommand + added it to the composite command (not relevant right now)
public LeftMenuViewModel(IRegionManager regionManager)
{
_regionManager = regionManager;
NavigateCommand = new DelegateCommand<object>(Navigate);
AppCommands.NavigateCommand.RegisterCommand(NavigateCommand);
}
+
public void Navigate (object navigatePath)
{
if (navigatePath !=null)
{
_regionManager.RequestNavigate(RegionNames.ContentRegion, navigatePath.ToString());
}
}
in my leftmenu view i have next piece of code:
dc:ButtonDropDown Header=" Enter New Client"
Style="{DynamicResource AppMenuCommandButton}"
Command="{x:Static Infrastracture:AppCommands.NavigateCommand}"
CommandParameter="ClientNewView"
Now, the command works and hits Navigate method in LeftMenuViewModel, it just refuses to load the content views (from different project) into Content Region, instead it just gives 'System.Object' in the content region.
I assume the problem is in this line of code:
_regionManager.RequestNavigate(RegionNames.ContentRegion, navigatePath.ToString());
we can't just load the views from different project?
Do I have to register the views with the container in the Content Views project? Like this:
_container.RegisterType<object, ClientNewView>(typeof (ClientNewView).FullName);?
thanks for any help !
I'm not sure what container you use, but with MEF you have to either:
a. Register library with container in bootstrapper
OR
b. Create separate project as a PRISM Module and add it to catalog. Then PRISM will import this library automatically
Now, this will solve your problem I think.
But if it doesn't - you need to trap navigation error. RequestNavigate swallows exceptions and to see them you need to subscribe for it like shown here:
http://blogs.southworks.net/gmaliandi/2011/08/how-to-prevent-region-navigation-from-hiding-exceptions-in-prism/

Eclipse RCP: How can I update a view when the selected editor changes?

This should be quite a common problem, but I couldn't find anything helpful on the topic.
We are developing an application with Eclipse RCP. The application shows data in an editor of which usually multiple instances are open. In an additional view you can edit the editor-values. When the values are changed in the view they are updated in the editor and it's dirty flag is set.
So far it works fine. What we're missing is: When another editor instance gets the focus, our view should show the data of this editor.
I managed to do that for two views. The second view is sucessfully updated using a TableViewer as selection Provider and registering a SelectionListener in the other view. I tried the same thing for the editor using a Viewer I subclassed from ContentViewer, but it didn't work.
Can this approach be working?
Or do I need a different approach on the problem?
May be you can subclass your view from PageBookView and then provide special adapter for your editor. Outline View is implemented using this approach.
Thank you cerealk, that was exactly what I needed. :-)
Update the View when another Editor is selected
public class myView {
// Create an IPartListener2
IPartListener2 pl = new IPartListener2() {
// If the Editor I'm interested in was updated ...
public void partActivated(IWorkbenchPartReference ref) {
IWorkbenchPart part = ref.getPart(true);
if (part instanceof DetailEditor) {
// ... update the view
Contact contactFromSelectedEditor = ((DetailEditor) part).detailComposite.contact;
detailComposite.update(contactFromSelectedEditor);
}
}
...
}
// Add the IPartListener2 to the page
IWorkbenchPage page = this.getSite().getPage();
page.addPartListener(pl);
}
Why use an IPartListener2 instead of an IPartListener
The IPartListener2 replaces IPartListener with 3.5.
As explained in this this answer:
You should always use IPartListener2 as it can handle part-change events on parts that have not yet been
created because they are hidden in a stack behind another part.
This
listener will also tell you when a part is made visible or hidden or
when an editor's input is changed.

Resources