I have a region within a region. Main region (which holds all the others) is named ContentRegion and the other one which I used to display partial info is named SettingsRegion.
Under my bootstrapper I have defined the following:
Prism.Regions.IRegionManager contentRegion = Container.TryResolve<Prism.Regions.IRegionManager>();
#region Register Multiple Regions
//contentRegion.RegisterViewWithRegion("ContentRegion", typeof(MainWindow));
contentRegion.RegisterViewWithRegion("SettingsRegion", typeof(SettingsView_MainPage));
#endregion
I can navigate from my ContentRegion to SettingsRegion without a problem like following:
_regionManager.RequestNavigate("ContentRegion", Experiences.Navigation.SettingsView_MainPage.ToString());
_regionManager.RequestNavigate("SettingsRegion", Experiences.NavigationSettings.SettingsView_ShiftSettings.ToString());
Everything renders great, I can see both views (contents) at the same time. However when I try to navigate back to main page like following
_regionManager.RequestNavigate("ContentRegion", Experiences.Navigation.MainPage.ToString());
It gives me the following error:
{"Region with the given name is already registered: SettingsRegion"}
I have read multiple articles regarding nested Regions, however I could not implement none. I should probably also mention that I am using Unity as well.
I am also attaching pictures so that my explanation is less confusing.
Main View / Main Window
Second view where I want to show some detail under the nested region
So in the end I had to do the following adjustments:
remove completely the following line from Bootstrapper (I don't understand why I don't need to register it).
contentRegion.RegisterViewWithRegion("SettingsRegion", typeof(SettingsView_MainPage));
Afterwards had to do slight change under my SecondPageViewModel; originally I was directly navigating to sub page under the constructor, but once I implemented INavigationAware and moved the navigation to OnNavigatedTo then it suddenly started working.
Related
I tried to put a second region inside one of my existing Prism views. Unfortunately, calling RequestNavigate with this region (and an externally-defined) view results in an ArgumentException that has the message. "Region with the given name is already registered". I am trying to figure out how to make it work.
The Prism App (.NET 6, WPF, Prism 8.1) is a simple wizard with Next/Previous buttons. The main window has a ContentControl as the main region. Very basic
<Window x:Class="MyApp.Views.MainWindow"
... blah blah blah...
>
<ContentControl x:Name="Page" Grid.Row="1"
prism:RegionManager.RegionName="Main"/>
... wizard next/prev buttons, other stuff ...
The app registers several views/view-models that I can navigate to in that main region. They are defined within the application assembly itself.
There is only one Prism view defined in another module. This all works great. I can navigate my main region to any of them with no problems.
RegionManager.RequestNavigate("Main", "WizardPage1"); // Defined in my app assembly
RegionManager.RequestNavigate("Main", "WizardPage2"); // Defined in my app assembly
RegionManager.RequestNavigate("Main", "ExternalView"); // Defined in another module.
The trouble came when I decided I wanted to use an internally-defined view to "host" the ExternalView. So I created the new internal Prism view, InternalHostView, and I just gave it its own ContentControl with a different region name
<UserControl x:Class="MyApp.Views.InternalHostView"
... blah blah blah
>
<ContentControl x:Name="SecondHost"
prism:RegionManager.RegionName="HostRegion"/>
When the user navigates to this new internal hosting view (in the InternalHostViewViewModel.OnNavigatedTo function), I thought I could just take the region manager and navigate this "hosting" region to the external view.
public void OnNavigatedTo(NavigationContext navigationContext)
{
// Navigate host region to the external Prism view
// Note: "RegionManager" obtained via constructor injection from Prism
RegionManager.RequestNavigate("HostRegion", "ExternalView");
}
Unfortunately This throws the aforementioned ArgumentException.
Region with the given name is already registered: HostRegion
I cannot change the externally defined view. It must remain external and it must remain a Prism view because it's used by another app. It registers itself as my internal Prism views do, via RegisterForNavigation
Is there a way to do what I want?
Searching for this error message yields a number of people with the same problem. But none of the solutions I've found (mainly trying to manually register the view myself) seem to help.
I have a a PopUp with a Region that contains another Region. This popup is invoked through the WPF Prism(MEF) InteractionRequest methodology. The structure looks like so:
PopUpUserControl
- ContentControl : Region(UserCatalogsCreateRegion)
- PopUpStageUserControl
- StackPanel
-ContentControl : Region(UserCatalogsCreateStackRegion) <--Disappearing Region
The problem manifests itself like this. When the application starts up and is running normally, I can list the Regions in the application and I can see that the RegionManager contains the Region named "UserCatalogsCreateStackRegion".
Now when I click the button that sets off the InteractionRequest for PopUpCreation, I can see that the list of Regions no longer contains "UserCatalogsCreateStackRegion". I verified that something is removing my Region because I added a CollectionListener to the Regions property of the RegionManager, and as soon as the Popup is created, my breakpoint is hit and the Notif..Action is "Remove" and the OldItem is the Region in question.
TL;DR Region disappears from RegionManager.Regions when the popup that contains said Region is created and invoked.
Any help is greatly appreciated. And I will try to answer as many other questions as possible as there is A LOT that can go wrong with a Region manager.
EDIT
Brian Lagunas' links pointed right to the doggone solution. This was the solution. My final working code for the PopUpStageControl looks like this, where ContentControl is the Region that kept "disappearing":
[ImportingConstructor]
public PopUpStageUserControl(IRegionManager regionManager)
{
InitializeComponent();
this.regionManager = regionManager;
//Fix Begin
RegionManager.SetRegionName(ContentControl, AppRegions.UserCatalogsCreateStackRegion);
RegionManager.SetRegionManager(ContentControl, regionManager);
//Fix End
RegionManager.SetRegionManager(this, regionManager);
RegionManager.UpdateRegions();
}
This is because a popup is not part of the visual tree, so the region manager will not be able to find the region. You will have to manually register the region. See these posts:
Region not loaded by the RegionManger
How to register regions inside user controls or control templates in RegionManager?
PRISM 6 Regions in WPF using DataTemplate/CustomControl
https://github.com/PrismLibrary/Prism/issues/251
Going off of my comment and then a quick Google result (old version of PRISM).
The IRegionMemberLifetime interface: Note also that the ModuleARibbonTab class implements the IRegionMemberLifetime interface. This interface is provided by Prism, and it controls whether a View is removed from a region when the user navigates away from the View.
By the sounds of it you might want to implement IRegionMemberLifetime and set KeepAlive appropriately--that might have an effect on when the RegionManager removes/persists the region.
Although you do not stated it in your posted code, I assume that you are using something like this when setting up InteractionRequest:
<prism:PopupWindowAction.WindowContent>
<inf:PopUpStageUserControl/>
</prism:PopupWindowAction.WindowContent>
So you have to be aware that at runtime Prism will replace all popup content with the one you specified in PopupWindowAction.WindowContent.
I am using MvvmCross to create a MonoTouch application. I have followed the the basic tutorial, and so far so good. The only problem is that my (initial and so far only) view is displayed with a top bar/navigation bar, which I don't want. I am able to hide the navigation bar by calling
this.NavigationController.NavigationBarHidden = true;
in the view controller's ViewDidLoad. I would prefer to not have to suppress the navigation bar, but rather that it not be there at all. The fact that it is appearing is suggesting that perhaps I am doing something wrong/inheriting from the wrong base classes?
Further details on my code:
The view controller inherits from MvxBindingTouchViewController.
My Setup class inherits from MvxBaseTouchBindingSetup (I will not be using TouchDialog anywhere, so am not inheriting from MvxTouchDialogBindingSetup).
Any help would be greatly appreciated! If I need to supply more details on my code, please let me know.
The Navigation bar is part of the UINavigationController which is used in the default Presenter.
The Presenter is the thing that decides how a View (a UIViewController) is shown - whether it is displayed in a popup, displayed as a Modal view, pushed into a navigation controller, etc.
If you want to customise the Presenter - e.g. so that it hides the navigation bar - then just switch in your own implementation in your AppDelegate.cs where you find the code:
// initialize app for single screen iPhone display
var presenter = new MvxTouchViewPresenter(this, _window);
var setup = new Setup(this, presenter);
setup.Initialize();
You can see some examples of custom presenters in the TwitterSearch and Conference samples. TwitterSearch uses different presenters for phone vs tablet; and Conference uses a presenter which is aware of multiple tabs, each of which contains a NavigationController.
There are also a few other questions around on custom presenters like MvvmCross Using a modal ViewController from a Tab and why does MvxModalSupportTouchViewPresenter in MvvmCross only support one modal view
In my MainPage.xaml, I created a Pivot Control: <controls:Pivot Title="Powder God" Name="PivotControl">.
My first pivot view is a HubTile that summarize all other individual pages. So my application bar will be different between the first pivot view and all other ones.
That's why I put my application bar in App.xaml's resource section, then load based on selected index of my pivot.
My question is:
In the application bar I will be using for all individual pages, I want to have a delete option, where I will remove that specific item (a view model) from my data context.
I know I can use PhoneApplicationFrame root = Application.Current.RootVisual as PhoneApplicationFrame; to access navigation services, but I don't know how can I reference to my pivot, so that I can get the selected index and proceed forward.
Thanks!
Using MVVM you SHOULDN'T do this:
((PageType)Application.Current.RootVisual).PivotControl. //Blah
PageType is whatever type PhoneApplicationFrame is that contains your PivotControl. If this doesn't work you need a Property in the RootVisual
PAGE
public Pivot MyPivot
{
get
{
return PivotControl;
}
}
APP
((PageType)RootVisual).MyPivot. //Blah
On one level Microsoft's suggestion of putting the ApplicationBar in App.xaml is great as it can be referenced from everywhere and would appear to encourage code reuse: however this question highlights the limit to this approach. An application bar is typically used to provide actions which are specific to the current page (or pivot item) and just because the buttons are the same you may not want the exact same code to run in each case.
In this case I think it would better to create a factory method that creates your common ApplicationBar with the click handlers you specify specific to your page/pivot item. For bonus points put the method in a new class (not App) so it doesn't get lost in all the boilerplate code there. Call this factory method in your page constructor and remember your ApplicationBar in your class. For multiple app bars, create them all up front and you can then easily switch between these app bars in your Pivot SelectionChanged code.
The alternative of creating the ApplicationBar in App.xaml and then retrieving this from the App.xaml.cs "Resources" ResourceDictionary in code, modifying the click callbacks, is more complicated in my opinion.
I wish they'd done a better job of implementing the ApplicationBar so people wouldn't want to do this. I've found that using the ApplicationBar forces you to add code to your Page.xaml.cs even if you use a framework like MVVM Light. This is still OK in MVVM as it's UI specific code that belongs in the View, but it makes things inconsistent if you're using ICommand everywhere else. Last time I decided it was better to create the entire ApplicationBar in code rather than hack this kind of thing via App.xaml.cs.
Update: There is a UserVoice request for a data bindable ApplicationBar.
What does InitializeComponent() do, and how does it work in WPF?
In general first, but I would especially be interested to know the gory details of order of construction, and what happens when there are Attached Properties.
The call to InitializeComponent() (which is usually called in the default constructor of at least Window and UserControl) is actually a method call to the partial class of the control (rather than a call up the object hierarchy as I first expected).
This method locates a URI to the XAML for the Window/UserControl that is loading, and passes it to the System.Windows.Application.LoadComponent() static method. LoadComponent() loads the XAML file that is located at the passed in URI, and converts it to an instance of the object that is specified by the root element of the XAML file.
In more detail, LoadComponent creates an instance of the XamlParser, and builds a tree of the XAML. Each node is parsed by the XamlParser.ProcessXamlNode(). This gets passed to the BamlRecordWriter class. Some time after this I get a bit lost in how the BAML is converted to objects, but this may be enough to help you on the path to enlightenment.
Note: Interestingly, the InitializeComponent is a method on the System.Windows.Markup.IComponentConnector interface, of which Window/UserControl implement in the partial generated class.
Looking at the code always helps too. That is, you can actually take a look at the generated partial class (that calls LoadComponent) by doing the following:
Go to the Solution Explorer pane in the Visual Studio solution that you are interested in.
There is a button in the tool bar of the Solution Explorer titled 'Show All Files'. Toggle that button.
Now, expand the obj folder and then the Debug or Release folder (or whatever configuration you are building) and you will see a file titled YourClass.g.cs.
The YourClass.g.cs ... is the code for generated partial class. Again, if you open that up you can see the InitializeComponent method and how it calls LoadComponent ... and much more.