Prism view containing own region throws exception on navigation - c#

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.

Related

Opening a second Window from MainWindow following MVVM and loose coupling

At first: This App and Question is for learning purpose
I'm on a new application and facing the problem that I want to open a Window when the user clicks on a Button in the MainView. In the past I'd have designed a Command which just creates the new Window and displays it
new RelayCommand((x)=>new SecondWindow().Show());
Now with this new Project I'm trying to fully decouple all classes from each other. To achieve this my App consists of 4 Assemblies (BL, COM, DAL and UI).
As in each WPF Application, the App starts with the MainWindow.xaml. The MainWindow.cs will create it's instance of MainWindowViewModel:
public ViewModel VM {get; private set;}
public class MainWindow(){
VM = new ViewModel();
InitializeComponent();
}
(which already violates loose coupling) (Any tips on how to make it better?)
My last attempt is to create an instance of my second Window inside my main window
<Window.Resources>
<local:SecondWindow x:Key="sw"/>
</Window.Resources>
and pass it as a CommandParameter to my Command
CommandParameter="{StaticResource sw}"/>
new RelayCommand((x)=> ((Window)x).Show());
This solution works but has one big disadvantage - the second window get's created immediately after the app starts - and so does it's ViewModel which starts some heavy processes (DB Connections etc.)
I've heard something abour IoC principle but I really don't know how to use it with an wpf application.
You are thinking along the right lines.... you basically have to create a List of ViewModels as your application starts up, then you can switch between them as the user presses buttons and pass the name of the ViewModel as a CommandParameter to your Command handler....
You might find this link to Rachel Lim's Blog
https://rachel53461.wordpress.com/2011/12/18/navigation-with-mvvm-2/
Also, I'm not going to post any code here coz it simply gets too complicated. So here is a download to just about the simplest example I could come up with
http://www.mediafire.com/download/3bubiq7s6xw7i73/Navigation1.rar
Download and un-RAR it (with win RAR) You will need to step though the code, figure out what its doing and how its doing it then modify it to suit your needs... Or modify your needs to suit the code.....
The example is a modification of Rachel Lim example. It simply contains Views and ViewModels, there are no Models or data. It demonstrates switching between two different Views.
UPDATE 1
With specific reference to the demo code.... Your VMs are added to a static collection of VMs (see AddViewModel function), each View ( the DataTemplate associates View with ViewModel) is selected when you click a button for example, by calling 'SelectViewCommand' which in turn sets Current_ViewModel to the selected ViewModel... the corrisponding ContentControl is then updated to display that currently selected View...
I know is confusing and very difficult to explain
When you press a button to 'change Views' you are actually changing the value of the property that your ContentControl is bound to, so you have to call the correct SelectViewCommand in the SAME instance of the class that your ContentControl is bound too...
In the demo you'll see that in the 'LogOn_View' I call
Command="{Binding DataContext.SelectViewCommand, ElementName=Base_V}"CommandParameter="Main_ViewModel"
Here I am calling the SelectViewCommand in the Base_ViewModel (x:Name="Base_V" in Base_View XAML), That's because I want to change the View that is displayed in the Base_View's 'ContentControl'
In Main_View I call
Command="{Binding SelectViewCommand}" CommandParameter="MainV1_ViewModel"
Here I am calling the SelectViewCommand in the Main_ViewModel, That's because I want to change the View displayed in the MainView's 'ContentControl'....
I typically create a WindowService class for managing window changes/dialogs in MVVM. Having "View" code in the ViewModel (i.e. Window.Show()) goes against MVVM principles. For example:
public class WindowService : IWindowService
{
public void ShowDialog<T>(ViewModelBase viewModel) where T : IApplicationDialog
{
IApplicationDialog dialog = (IApplicationDialog)Activator.CreateInstance(typeof(T));
dialog.Show();
}
}
And then your call from the ViewModel would look something like:
windowService.ShowDialog<SecondWindow>(new SecondWindowViewModel());
If you're using DI, you can pass a reference to the IoC container to the window service and create the window instances from that rather than using Activator.CreateInstance (i prefer the DI approach personally)

prism 6 nested regions navigation

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.

PRISM Region Disappears when Popup is Created

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.

MvvmCross and MonoTouch: remove top bar

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

Access Pivot Control from App.xaml.cs

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.

Categories

Resources