The project is based on PRISM.
I've got a main region in the TabControl in the Shell.
<!-- Main Region : A tab control -->
<TabControl prism:RegionManager.RegionName="MainRegion" >
</TabControl>
Then, I've got several Modules where all of them contains the same views. And must be added to the tab control region.
|_ModuleA
| |_ViewA
|
|_ModuleB
|_ViewB
Check please the image. In tab ModuleA, it shows in the left of the tab content ViewA, and the right is a generic view because all the tabs must show the same UI.
How can I do this?
I guess 'lecrank' already answered the question. There's no problem to add Region inside other Region or View. What I did in my project:
Every Module receive a copy of IRegionManager and IEventAggregator objects in constructor (I use MEF)
In Initialize() function every Module register it own Regions with it own View, etc, etc...
Whenever event or program flow should be passed to another Module (for example) then I just .Publish() predefined event (CompositePresentationEvent) with parameters.
The Shell itslef it's just empty template that defined just couple 'global' Regions (like TabControl in your scenario) and everything that displayed on it is coming from different Modules Views and not implemented in Shell class or even in Shell containing project.
So feel free to define and implement Regions and Views in another modules - just pass somehow the IRegionManager and IEventAggregator instances into Module class.
Your question is a bit odd, but if i'm not mistaken u should look for 'composite presenters'. A presenter that registers a new region to the regionmanager where other views can be shown. U can make a region locally scoped as well. For some more information see http://msdn.microsoft.com/en-us/magazine/cc785479.aspx
And a good place to find answers for Prism is on the compositewpf codeplex site, someone asking perhaps the same question: http://compositewpf.codeplex.com/discussions/273120
Related
I've created a dialog service using interfaces to show custom dialog/confirmation boxes (I know that Prism comes with this ability but the dialog boxes don't appear to be customizable and don't match my layout/color scheme). The dialog service has a register function that registers the dialog view type with dialog viewmodel type in a dictionary. This is done so that a viewmodel can be passed in and in a loosely coupled fashion, an instance of the needed view can be created. The code looks like this:
private readonly IDialogService dialogService = new DialogService(Application.Current.MainWindow);
// Registers the confirmation window viewmodel with the confirmation window view
dialogService.Register<ConfirmationWindowViewModel, ConfirmationWindow>();
so my initial thought was to try to create this in the unity bootstrapper (because of the registration passing in views and viewmodels) but I can't seem to find a way to do that and pass in the dialog service.
I must note that the constructor for the main window viewmodel also injects the region manager and the event aggregator for Prism; I had tried creating an instance in the bootstrapper and registering the instance but the creation of the region manager vs the injection causes errors. If I declare and initialize the dialog service in the main window viewmodel it of course works but from my understanding of MVVM we don't want the viewmodels to have any knowledge of the views so I'm trying to find another way to do it, without breaking IoC for region manager and event aggregator.
I am new to MVVM and Prism/Unity so my grasp of these concepts isn't fully solidified yet.
I know that Prism comes with this ability but the dialog boxes don't appear to be customizable and don't match my layout/color scheme
You can create whatever you like as dialog, just derive from PopupWindowAction and override CreateWindow (and other methods as needed) to create the dialog you always wanted.
In case anyone sees this later and is curious, my end decision was to get rid of the 'Register' function altogether in favor of a solid convention instead.
Previously, I would use this function and kept a dictionary of all the registered views/viewmodels:
dialogService.Register<ConfirmationWindowViewModel, ConfirmationWindow>();
this would register take the and store them in the dictionary so I could later pass in a viewmodel and create an instance of the appropriate confirmation message view. Instead I removed all code regarding this part of the solution and replaced it with some reflection mixed in with naming conventions.
Step 1: Ensure all views are named with the suffix View at the end.
Step 2: Ensure all viewmodels are named with the suffix ViewModel at the end.
Step 3: Ensure these are all in appropriately named namespaces (views in views namespace and viewmodels in viewmodels namespace).
(most of this ^^ is done anyway)
Final Step: Replaced dictionary with this code:
var viewTypeName = viewModel.GetType().AssemblyQualifiedName.Replace("Model", "");
var viewType = Type.GetType(viewTypeName);
in the dialog interface. Now, no matter what viewmodel is passed in, it will pull the appropriate view with less code and no necessary linking as was done before.
I'm working on a project in WPF and I'm not really familiar with it.
I have built the program, but I'm dissatisfied with the navigation.
It's a simple program, a couple of buttons which takes you to different pages. Changing page have I solved by the following:
Menu main = new Menu();
App.Current.MainWindow = main;
this.Close();
main.Show();
This is probably very incorrect. Any knowledge of standard practice for code behind or MVVM?
I had a project where i used the standard method Visibilty and changed it between collapsed and visible.
So three pages => 3 Containers
Button1
-- Show Container1 Collapse Container2,3
Button2
-- Show Container2 Collapse Container1,3
Button3
-- Show Container3 Collapse Container1,2
If you have a lot of pages this is not a nice way to do it, but for 2 to 5 pages its ok.
You could take a look at Paul Stovell's blog post for more information about the common navigations options that are available in WPF: http://paulstovell.com/blog/wpf-navigation
You could implement an interface in the view where the Frame is defined and then inject the view model with this interface to be able to use the Frame for navigation. There is an example available here: https://social.msdn.microsoft.com/Forums/sqlserver/en-US/b09bbfd4-05ee-4f62-b5df-77c0792e6ad7/how-to-refresh-the-frame-using-a-view-model-in-c?forum=wpf
I have a WPF application that provides navigation between few pages. The MainWindow is the Window object contains a Frame object. I then have few Page objects. I need to implement a StatusBar where some text will be updated (in a TextBlock) based on what action user has taken on a particular page.
Should my StatusBar be declared in the MainWindow or there is any better place for it?
How I will be able to access that TextBlock in StatusBar from various Pages?
What usually works for me is either pub-sub or dependency injection:
At first you might give your statusbar its own viewmodel. This would be composed into the shell view of your application, probably your MainWindow. I usually have a shell viewmodel comprising a toolbar or ribbon, a statusbar and, taking the remaining space, an IShellContent container. So, to answer your first question, I would declare it in its own view, give it its own viewmodel and compose it into your MainWindow.
The second problem can be solved in different ways:
Either give your statusbar viewmodel an interface, e.g. IStatusBar, and configure your dependency injection container to bind the viewmodel as singleton. Every viewmodel that needs to output status messages could use it via constructor injection, like this:
public MyViewModel(IStatusBar statusBar)
{
this.statusBar = statusBar;
statusBar.ShowMessage("Creating new MyViewModel...");
}
Or you could use a message bus infrastructure that comes with many MVVM frameworks today. Your statusbar viewmodel would subscribe a StatusMessage, and whenever something needs to post a status message it would create a new StatusMessage and publish it, like this:
public MyViewModel(IMessageBus bus)
{
this.bus = bus;
bus.Publish(new StatusMessage("Text"));
}
I would go for the first solution (dependency injection) because it is easier testable.
We're using Castle Windsor and Prism 4 (Feb 2010). We're using the Windsor bootstrapper that makes Castle play nice with Prism that was released in the CompositeWPFContrib package.
I'm trying to define regions on my main Shell's XAML. If I define one region, like so:
<ContentControl prism:RegionManager.RegionName="{x:Static core:RegionNames.ToolBarRegion}"
DockPanel.Dock="Top"/>
And then do the following in one of my Modules Initialize method:
_regionManager.Regions[RegionNames.ToolBarRegion].Add(typeof(SomeView));
...life is good.
However, as soon as I add another region in the Shell's XAML:
<ContentControl prism:RegionManager.RegionName="{x:Static core:RegionNames.WorkspaceRegion}"
DockPanel.Dock="Bottom"/>
And then do:
_regionManager.Regions[RegionNames.WorkspaceRegion].Add(typeof(SomeOtherView));
...I get the error: "The region manager does not contain the ToolBarRegion region."
What gives? As soon as I comment out the second region it finds the first, when I add the second region back in it blows up, as if the RegionManager refuses to hold a collection of regions. It should be said that this is my first foray into both Castle Windsor and Prism, so it's not out of the realm of possibility that I'm missing something painfully obvious here. Any light that could be shed on this would be most helpful.
Are you sure it's DockPanel that you are adding your controls to? Maybe your container is a content control itself (kind of control that accepts only one child)?
Also, you could try register your region manager in the bootstrapper:
RegionManager.SetRegionManager(shell, this.Container.Resolve<IRegionManager>());
See the following questions:
Cannot find Region in RegionManager (using PRISM)
WPF, Prism v2, Region in a modal dialog, add region in code behind
EDIT
I looked at the sample solution (link in comments), and found out that your view injection code gets executed before main view is created. Your module initializers get called in StartRuntime->CreatePrismBootStrapper, and DisplayRootView (which creates your shell) is called later. Of course it can't find the region when the shell hasn't been created yet.
If all you want to register your subcontrols in module initialize code, view discovery is more suitable - it doesn't require your shell to be already created. View injection is better when you need to switch views based on user input (in this case making sure that containing control had been registered is up to you).
You have several options:
Use view discovery - as you did in the sample solution.
Create and register your Shell instance before loading your modules. base.DisplayRootView() should be able to find it in the container so It wouldn't create another. One way to do, but I'm not sure if best:
PrismBootstrapper.cs:
protected override DependencyObject CreateShell()
{
Thor.Application.Views.ShellView view = new Thor.Application.Views.ShellView();
_container.Register(Castle.MicroKernel.Registration.Component.For<Thor.Application.Views.ShellView>().Instance(view));
// _container.Resolve<Thor.Application.Views.ShellView>();
return view;
}
.3. CreatePrismBootstrapper() after base.DisplayRootView ? It doesn't work (NullPointerException on ServiceLocator and I'm not sure if it would make sense since I'm not really familiar with libraries used by you except Prism...
Hope this helps...
I was working on a WPF project today that had a main nav window and then 4 pages that were loaded within this main window (using NavigationService.Nagivate...).. Within the XAML this created a lot of duplicate code so I wanted to refactor the menu into a user control that I could then bind to each page. I tried to create a class to handle the navigation and loading of each page but I discovered that NavigationServices is a sealed class and cannot be instantiated.
Would anyone please provide a suggestion/solution on how to create a usercontrol with a menu item that will allow the ability to navigate to new pages within the project. I have been able to do this within pages and the direct code behind but I have not had any luck trying to separate the two. If this is too vague please let me know and I will provide more deails with code samples.
Thanks in advance
Create a NavigationService dependency property on your user control. Then, when you instantiate your control, bind this property to the NavigationService of the container where the navigation should occur. For example, a page might display the user control like this:
<local:NavBox NavigationService="{Binding NavigationService, RelativeSource={RelativeSource AncestorType={x:Type NavigationWindow}}}" />
Now when your UserControl calls Navigate on its NavigationService, that will effectively call Navigate on the containing NavigationWindow's NavigationService. (This can be modified in the obvious way to support Frame or Page instead of NavigationWindow.)