RegionManager.Regions never contains my ChildWindow regions - silverlight

I've be struggling for a while trying to make it work. Basically I have a Silverlight application using MVVM/PRISM/Unity combination.
My shell consists by two Regions RootContent and RootMenu. My RegionManager.Regions are able to see those two regions just fine, and the application runs correctly.
The problem starts when one of my Views inside the RootContent opens a ChildWindow, it contains more two Regions, as follows:
<ContentControl Region:RegionManager.RegionName="WOFSCustomerLookup" />
<ContentControl Region:RegionManager.RegionName="WOFSCustomerView" />
The ViewModel of this View that has this XAML above, even inheriting and properly resolved, the IRegionManager.Regions collection do not contains those two new Regions above, just the RootContent and RootMenu.
More Information
This is How my ChildWindow is called (it calls the "View"):
ChildWindow editor = this.container.Resolve<WorkOrderFieldServiceEditor>();
editor.show();
And this is the Constructor of my ViewModel:
public WorkOrderFieldServiceViewModel(IUnityContainer container, IRegionManager regionManager)
{
this.container = container;
this.regionManager = regionManager;
// Still have just the two Root regions:
// this.regionManager.Regions[]
}
Did I miss anything?

Pretty sure the problem is because you are not showing the WorkOrderFieldServiceEditor view through Prism but are just getting an instance of it through the container and then calling Show method directly on it. So, Prism is not really involved. When the main Shell is created through the bootstrapper, the regions defined in the View are then created in the region manager. So, you will need to look at how you Navigate to a popup window using Prism and not call the Show method directly.
Checkout the RegionPopupBehaviors.cs file in the StockTrader reference application.
http://msdn.microsoft.com/en-us/library/ff921074(v=PandP.40).aspx

Related

Main Window starts to depend on too many things, but I don't want a service locator

I am creating a WPF application. Naturally my entry point is MainWindow.xaml, which is opened up by App.xaml
var mainWindow = container.Resolve<MainWindow>();
Application.Current.MainWindow = mainWindow;
Application.Current.MainWindow.Show();
I am using Dependency Injection and so far all the dependencies are passed as parameters in the ctor of the MainWindow's View Model.
i.e. my Main Window is
public partial class MainWindow : MetroWindow
{
private readonly MainWindowModel mainViewModel;
public MainWindow(MainWindowModel mainViewModel)
{
and its View model is:
public MainWindowModel(IDataRepository dataRepo, ICommand command1, ICommand command2, etc ...)
{
However, I am now starting to realize this might be a problem. Given that the MainWindow is the entry point to the entire app, it seems like any dependency, anywhere in the application will have to first pass through the MainWindow View Model constructor. This seems crazy.
I am coming from the background of ASP.NET MVC and there we have Controllers, which receive only the dependencies that they need. i.e. the concept of a main entry point there is missing and this makes things easier and more manageable.
Here is an example in my WPF app. A control, on the Main View needs to open up a dialog. This dialog is another Window and of course that window receives its ViewModel in its ctor. To me, it seems like to be able to resolve the dialog properly, I need to pass it through the Main Window View Model ctor first, keep it as private readonly field of the Main Window View Model and launch it when necessary. Ok, but what if I have 100 dialogs. That's just one of the examples. I have such issue with the ICommand implementations too.
To sum up my question:
How do I manage the dependencies in WPF properly, without using the Service Locator anti-pattern and without passing every single abstraction through the ctor of the main window view model? I could very easily pass a Container around and let, e.g., the create ABC command solve the ABCDialog before opening it, but I feel this will cause more issues than it would solve.
I am probably doing something wrong. Please advise me what is the best practice.

Using MVVM show new window and get updates data

I'm working on a WPF MVVM application. I'm showing some data in a datagrid. I've two buttons to Add and Edit the selected record. I've data in ViewModel and I've to show another window (view) and make sure that ViewModels should have no information about views.
Where should I create its view and viewmodel?
How to get the data back and update datagrid?
How can I achieve this in MVVM?
We have not yet decided to use any framework, so I've to create my own interface.
Note: This ended up being quite a long answer - please ask me if anything is unclear
The implementation of dialog windows is a contentious issue in MVVM designs, and different people use different approaches.
Like you, I've decided not to use any framework and implement most things by hand. When it comes to dialog windows, I choose to be pragmatic about my implementation of MVVM, by launching the Dialog Window from inside my ViewModel. Also, I allow each Dialog ViewModel to have a reference to the Window it is displayed in, so it can close it when appropriate (details below). This breaks some of the strict MVVM "rules", but it gets the job done.
The main downside of this is that it might break unit testing if you are testing something that goes through a dialog. However, you can go a long way without running into that problem and it has not bothered me yet.
I've built up a bit of a library of dialog ViewModels which I can easily extend. It's way too much code to post here, but I'll show you the highlights.
Base ViewModel for Dialogs
Each of my dialog windows has a ViewModel that inherits from DialogViewModelBase, which is similiar to my regular ViewModelBase in that it provides support for INotifyPropertyChanged etc. The interesting part is this public method, which I call from wherever to launch the Dialog:
/// <summary>
/// Creates window instance for this dialog viewmodel and displays it, getting the dialog result.
/// </summary>
public void ShowDialogWindow()
{
// This is a property of the DialogViewModelBase class - thus, each DialogViewModel holds a reference to its own DialogWindow:
this.DialogWindow = new Dialogs.Views.DialogWindow();
// Tell the DialogWindow to display this ViewModel:
this.DialogWindow.DataContext = this;
// Launch the Window, using a method of the Window baseclass, that only returns when the window is closed:
this.DialogWindow.ShowDialog();
}
Window launched in the above method will close when its Window.DialogResult property is set. This is why the DialogWindow is a property of the DialogViewModelBase class - when the subclassing dialog ViewModel wants to close the dialog window, it simply sets the result:
protected void CloseDialogWithResult(bool dialogWindowResult)
{
// Setting this property automatically closes the dialog window:
this.DialogWindow.DialogResult = dialogWindowResult;
}
Host Window for Dialog Views
The Dialogs.Views.DialogWindow class that the ShowDialogWindow method instantiates is defined in XAML and is a subclass of Window. It has two important features. The first is that it's primary content element is simply a ContentControl that binds to the current context. This allows me to define different Views for different subclasses of DialogViewModelBase, and the DialogWindow will host the corresponding View based on the type of the context:
<ContentControl Content="{Binding}" /> <!-- In reality this is inside a border etc but its simplified here for demonstration -->
The second important feature of the DialogWindow XAML is that it defines which dialog Views go with which dialog ViewModels. Here is a sample:
<Window.Resources>
<!-- DEFAULT ViewModel-View TEMPLATES -->
<DataTemplate DataType="{x:Type dialogs:YesNoMessageBoxDialogViewModel}">
<views:MessageBoxView />
</DataTemplate>
<DataTemplate DataType="{x:Type dialogs:ErrorDialogViewModel}">
<views:ErrorDialogView/>
</DataTemplate>
</Window.Resources>
What all this does, is that I can define dialogs as subclasses to DialogViewModelBase and implement a View for each, and then tell DialogWindow which View its ContentControl must show for which dialog ViewModel.
Launching a Dialog and getting results
Below is a sample from one of my application ViewModels, in which I launch a Dialog Window that allows the user to select an Asset Type for creation:
public void CreateNewAsset()
{
// Instantiate desired Dialog ViewModel:
Dialogs.NewAssetTypeSelectionDialogViewModel dialog = new Dialogs.NewAssetTypeSelectionDialogViewModel();
// Launch Dialog by calling method on Dialog base class:
dialog.ShowDialogWindow();
// Execution will halt here until the Dialog window closes...
// The user's selection is stored in a property on the dialog ViewModel, and can now be retrieved:
CalculatorBase.AssetTypeEnum newAssetType = dialog.AssetType;
switch (newAssetType)
{
// Do stuff based on user's selection...
}
}
PS: I should really write a blog entry about this - when I do, I will post the link here, as the blog entry will probably have more complete code samples.
It depends how you are handling the data. I will assume that changes made in the popup window can be accepted only when user clicks something like save in other case they should be discarded.
So firstly, I would suggest using MVC approach as controller is perfect for such tasks. You build viewmodels in it, assign them o views and show the views. VM's simply keeps data and commands, commands execute methods are kept in controller. In other words you have singleton class which manages your VM's and views.
You should check out Prism framework. It offers great things like view regios where you can inject different user controls on the runtime, commanding and MVC layering out of the box alongside IOC and DI patterns.

DockPanel and User Control UI with Ribbon Control

I have a ribbon control with multiple buttons that need to display different windows/user control in the main area of the window. I'm thinking about creating a user control for each 'functional area' that relates to it's button in the ribbon.
Something like
Public Class RibbonViewModel
Public ReadOnly ucPreferences As UserControl = New ucPreferences
Public ReadOnly ucMain As UserControl = New ucMain
End Class
User clicks the Preference button and I'll do
dockMain.Children.Clear()
dockMain.Children.Add(oRibbon.ucPreferences)
This is working as far as the layout goes but I'm not sure what issues I might run into.
This will certainly work; however a more robust approach would be making use of Prism...more specifically the IRegionManager.
What this provides is a way to define regions within your application where you can push content to live within the region. An example would be...
<ad:DockingManager Grid.Row="1" Margin="0">
<ad:DocumentPane x:Name="WorkspaceRegion" prismrgn:RegionManager.RegionName="WorkspaceRegion"/>
</ad:DockingManager>
...where ad is the namespace for the AvalonDock assembly; however it could be your DockPanel just the same. This can then be referenced in the code behind as follows...
_regionManager.AddToRegion("WorkspaceRegion", workspaceContent);
_regionManager.Regions["WorkspaceRegion"].Activate(workspaceContent);
...where _regionManager is an instance received via DI within the contructor of the object placing workspaceContent in the WorkspaceRegion.
This provides nice decoupling with regard to the Ribbon action which will drive the interface to be displayed for that given action. It also provides an abstraction on the region itself, ie..is it a DockPanel or some other control.
As stated previously your initial approach will work. Whether you should opt for a framework such as Prism to isolate concerns as well as increased functionality is dependent on the scale of this project both now and in the future.

Open second shell in prism v4 WPF app (regions)

I've got a prism app, containing a Shell.xaml (with a MainRegion), ShellViewModel.cs.
This Shell window is opened when the app starts. Now I want to open a second Popup-Window containing the very same shell window (Shell.xaml, ShellViewModel).
The Shell definition is like in the prism StockTraderRI example. Shell.xaml contains a MainRegion (very simplified source):
<Window x:Class="Bsoft.Test.Shell"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:cal="http://www.codeplex.com/CompositeWPF"
Title="MainWindow" Height="550" Width="825">
<Grid>
<ContentControl cal:RegionManager.RegionName="MainRegion"/>
</Grid>
</Window>
Code behind contains just the basic ViewModel reference:
namespace Bsoft.Test.bmedApp
{
[Export]
public partial class Shell : Window
{
[ImportingConstructor]
public Shell()
{
InitializeComponent();
}
[Import]
ShellViewModel ViewModel
{
set
{
this.DataContext = value;
}
}
}
}
The ShellViewModel is automatically inserted by the MEF loader:
namespace Bsoft.Test.bmedApp
{
[Export]
public class ShellViewModel : NotificationObject
{
[ImportingConstructor]
public ShellViewModel()
{
}
}
}
This does work like intended.
Now I want to open the shell window a second time as a popup window. It's easy enough to mark the Shell and ViewModel as not being shared using:
[PartCreationPolicy(CreationPolicy.NonShared)]
But my problems are:
1) I load other View(Models) into the MainRegion. How do I tell the program if the View(Model) should be loaded into the main Shell MainRegion or into the popup window MainRegion? I guess I need scoped RegionManagers, but I got no clue how to use them for this.
2) I've got some events (EventAggregator) for the Views loaded into a region to communicate notification and commands (status update, view closing, errors) for the Shell to report. How can I seperate the main shell events from the popup window events (since both are the same shell)?
I want to be able to open several of the popup windows, so using different region names for both is not enough for me, I need more separation. Maybe there is a way to create a separate internal prism/mef/region/container framework??
I do not completely understand what do you mean by opening two shells ?
If you run your silverlight application in two different windows or you have 2 instances of your WPF app then your Shells do not conflict.
Even if you have one application with 2 instances of Bootstrapper there is no conflict - your two shells work completely independently.
Let me know whether this help.
What you are trying to achieve is possible, although there might be some things I don't completely understand about your approach.
I assume that when you are talking about having two Shells you actually mean having two active windows at the same time.
There are many ways to achieve this in Prism, so let's get on with your doubts.
For (1) the best thing I can think of is creating a different instance of the Region manager an attaching it to the other Shell (the popup one). This is similar to working with scoped regions (as you would have a separate RegionManager), but you create the manager and attach it to the Shell instead. Then register the new RegionManager in MEF with a string Id so you can differentiate it from the MainWindow RegionManager and simply add regions to the correct region manager.
(2) is a different subject, as you are trying to get the same code to behave differently. Perhaps, if you require such different behaviors, using the same Shell class for both windows is not the best approach. If you require this kind of differentiability but would still like to reuse code I'd recommend using some form of inheritance and combining virtual methods in a BaseShell with template methods to perform the things that are different for each Shell.
I hope this illustrates my point.

Prism regions not displaying registered views

I'm using PRISM in a SilverLight 4 application. I have a problem where views registered to some regions doesn't get displayed.
When loading a module at startup I register some views to regions as follows:
RegionManager.RegisterViewWithRegion("MyRegion1", typeof(IMySubView1));
RegionManager.RegisterViewWithRegion("MyRegion2", typeof(IMySubView2));
I have a view implementing an interface called IMyView, where the xaml have two contentcontrols with regions defined in a grid like this:
<ContentControl Regions:RegionManager.RegionName="MyRegion1" HorizontalContentAlignment="Stretch" VerticalContentAlignment="Stretch" Grid.Row="0" Grid.RowSpan="1"/>
<ContentControl Regions:RegionManager.RegionName="MyRegion2" HorizontalContentAlignment="Stretch" VerticalContentAlignment="Stretch" Grid.Row="1" Grid.RowSpan="1"/>
I have tried two different methods for adding the view to the main region. Both adds the view and basic elements such as buttons get displayed, but the regions defined in the view does not get filled with associated views.
Method 1:
object obj = _container.Resolve<IMyView>();
IRegion mainRegion = _regionManager.Regions["MainViewRegion"];
IRegionManager scoped = mainRegion.Add(obj, "test", true);
mainRegion.Activate(obj);
// Enabling the following call, it will fail saying the region MyRegion1 does not exist. Feels like it should?
// IRegion myRegion = scoped.Regions["MyRegion1"];
Method 2:
object obj = _container.Resolve<IMyView>();
_regionManager.AddToRegion("MainViewRegion", obj);
_regionManager.Regions["MainViewRegion"].Activate(obj);
It feels like the regions defined in the xaml file doesn't get registered, and because of that the registered views do not get displayed.
The MainViewRegion is defined in the shell in a TabControl as this:
<TabControl Margin="8,0,8,8" Regions:RegionManager.RegionName="MainViewRegion">
Any suggestions on solving my problem will be greatly appreciated!
I'm facing the same problem. The idea is that for whatever reason view injection {mainRegion.Add(obj, "test", true)} for already created regions doesn't show the view. A workaround that worked for me, is to create the region from code, and then inject the view. Something like this:
Microsoft.Practices.Composite.Presentation.Regions.RegionManager.SetRegionManager(headerRegionContainer, _RegionManager);
Microsoft.Practices.Composite.Presentation.Regions.RegionManager.SetRegionName(headerRegionContainer, regionName);
var view = _UnityContainer.Resolve(bag.HeaderViewType);
_RegionManager.Regions[regionName].Add(view);
_RegionManager.Regions[regionName].Activate(view);
Unfortunately for me I can't reach my goal this way, but maybe you can.
Regards,
Levente
After many hours of troubleshooting I found something.
In Composite.Presentation\Regions\RegionManager.cs there is a method called IsInDesignMode. When a region is about to be created this method is called, and if this method returns true the region is not created. See below:
private static void OnSetRegionNameCallback(DependencyObject element, DependencyPropertyChangedEventArgs args)
{
if (!IsInDesignMode(element))
{
CreateRegion(element);
}
}
private static bool IsInDesignMode(DependencyObject element)
{
// Due to a known issue in Cider, GetIsInDesignMode attached property value is not enough to know if it's in design mode.
return DesignerProperties.GetIsInDesignMode(element) || Application.Current == null
|| Application.Current.GetType() == typeof(Application);
}
When our silverlight application starts up and the regions in the shell gets created everything is fine, the Application.Current property is of type "MyName.Shell.App". But when a view gets added after startup, as a response to user input, the Application.Current type is suddenly of type "Application", and thus the IsInDesignMode method returns true and the regions are not created.
If I remove the Application.Current conditions everything works as expected. So the question is, is there something wrong in my application or is there something wrong in the prism source code?
where is your _regionManager coming from ?
Did you write a proper BootStrapper
?
You need to write a class inheriting from MefBootstrapper or UnityBootstrapper (or a custom one if you're not using neither of those IoC/Extension framework) in order to register all your needed types within the IoC container.
could you post the BootStrapper code ?
The problem is gone in Prism version 4.0, I was running Prism version 2.2 when the problem occured.

Resources