How to manage multiple windows in MVVM - c#

I am aware there are a couple of questions similar to this one, however I have not quite been able to find a definitive answer. I'm trying to dive in with MVVM, and keep things as pure as possible, but not sure how exactly to go about launching/closing windows while sticking to the pattern.
My original thinking was data bound commands to the ViewModel triggering code to start a new View, with the View's DataContext then set to it's ViewModel via XAML. But this violates pure MVVM I think...
After some googling/reading answers I came across the concept of a WindowManager (like in CaliburnMicro), now if I was to implement one of these in a vanilla MVVM project, does this go in with my ViewModels? or just in the core of my application? I'm currently separating out my project into a Model assembly/project, ViewModel assembly/project and View assembly/project. Should this go into a different, "Core" assembly?
Which leads on a bit to my next question (relates somewhat to the above), how do I launch my application from an MVVM point of view? Initially I would launch my MainView.xaml from App.xaml, and the DataContext in the XAML would attach the assigned ViewModel. If I add a WindowManager, is this the first thing that is launched by my Application? Do I do this from the code behind of App.xaml.cs?

Well it mainly depends on how your application looks like (i.e. how many windows opened at the same time, modal windows or not...etc).
A general recommendation I would give is to not try to do "pure" MVVM ; I often read things like "there should be ZERO code-behind"...etc., I disagree.
I'm currently separating out my project into a Model assembly/project,
ViewModel assembly/project and View assembly/project. Should this go
into a different, "Core" assembly?
Separating views and ViewModels into different assemblies is the best thing you can do to ensure you won't ever reference something related to the views in your viewModel. You'll be fine with this strong separation.
Separating Model from ViewModel using two different assemblies could be a good idea too, but it depends on what your model looks like. I personally like 3-tier architectures, so generally my model is the WCF client proxies and are indeed stored in their own assembly.
A "Core" assembly is always a good idea anyway (IMHO), but only to expose basic utility methods that can be used in all the layers of your application (such as basic extension methods....etc.).
Now for your questions about views (how to show them...etc), I would say do simple. Personally I like instantiating my ViewModels in the code-behind of my Views. I also often use events in my ViewModels so the associated view is notified it should open another view for example.
For example, the scenario you have a MainWindow that should shows a child window when the user click on a button:
// Main viewModel
public MainViewModel : ViewModelBase
{
...
// EventArgs<T> inherits from EventArgs and contains a EventArgsData property containing the T instance
public event EventHandler<EventArgs<MyPopupViewModel>> ConfirmationRequested;
...
// Called when ICommand is executed thanks to RelayCommands
public void DoSomething()
{
if (this.ConfirmationRequested != null)
{
var vm = new MyPopupViewModel
{
// Initializes property of "child" viewmodel depending
// on the current viewModel state
};
this.ConfirmationRequested(this, new EventArgs<MyPopupViewModel>(vm));
}
}
}
...
// Main View
public partial class MainWindow : Window
{
public public MainWindow()
{
this.InitializeComponent();
// Instantiates the viewModel here
this.ViewModel = new MainViewModel();
// Attaches event handlers
this.ViewModel.ConfirmationRequested += (sender, e) =>
{
// Shows the child Window here
// Pass the viewModel in the constructor of the Window
var myPopup = new PopupWindow(e.EventArgsData);
myPopup.Show();
};
}
public MainViewModel ViewModel { get; private set; }
}
// App.xaml, starts MainWindow by setting the StartupUri
<Application x:Class="XXX.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
...
StartupUri="Views/MainWindow.xaml">

Related

Provide "trigger-method" to module-(sub)-view model in WPF and MVVM

I'm got an other question for my WPF/MVVM application I'm working on since a while.
The main idea is to use a main window providing a navigation bar and a ContentControl.
The different "Modules" are all built as UserControl with each its own ViewModel.
The main call from the main viewmodel to start a module is
private void ShowAddressModule() {
ContentControlBindingProperty = new AddressModule(new AddressModuleViewModel);
}
In the real application the viewmodels are pre-loaded and so on, but the start is more or less the same.
The main view model contains a boolean property LongRunningOperation to do multiple operations on the main window while any long running operation.
As example showing a loading image or disable the main navigation while loading a new module or whatever.
So my idea is to provide a possibility to the modules (their view models) to active this "mode".
Example how it could look in the modules view model:
private void LoadContactList() {
MainWindow.LongRunningOperation = true;
LoadAllContactsInAThread(); /*Takes a long time*/
MainWindow.LongRunningOperation = false;
}
I tried to mark the property as static and public, but this will not work because of the OnPropertyChanged event.
If possible it would be great if the solution could be applied also to methods (including parameters) from the main window - so (as example) the modules could use as example the parents statusbar or so.
MainWindow.ShowErrorMessageInStatusBar("The error xyz occured!");
Hopefully I described good enought, what's my idea...
And hopefully anybody could provide me the needed tip how to handle this requirement.
Thanks in advance for any hints
Regards Markus
Each module could raise an event to indicate the start of a long running operation, and raise an event to indicate the end of a long running operation. Your main view model, when loading modules for the first time, could hook-up to these events and react to them accordingly.
Your sub view model would have some events like this:
Sub view model
public delegate void OnLongRunningOperationStartedEventHandler(object sender);
public delegate void OnLongRunningOperationFinishedEventHandler(object sender);
public event OnLongRunningOperationStartedEventHandler OnLongRunningOperationStarted;
public event OnLongRunningOperationFinishedEventHandler OnLongRunningOperationFinished;
private void LoadContactList() {
OnLongRunningOperationStarted?.Invoke(this);
LoadAllContactsInAThread(); /*Takes a long time*/
OnLongRunningOperationFinished.Invoke(this);
}
And your main view model will hook-up to them like this:
Main View Model
public bool LongRunningOperation { get; private set; }
// Keep track of the number of modules currently running long operations
private int _countLongRunningOperations = 0;
public LoadSubModules(){
// Depending on how you load your sub modules, this piece of code could move around
foreach (var module in submodules){
module.OnLongRunningOperationStarted += Module_LongOperationStarted;
module.OnLongRunningOperationFinished += Module_LongOperationFinished;
}
}
private void Module_LongOperationStarted(object sender){
_countLongRunningOperations += 1;
LongRunningOperation = true;
}
private void Module_LongOperationFinished(object sender){
_countLongRunningOperations -= 1;
if (_countLongRunningOperations == 0) {
LongRunningOperation = false;
}
The same principle (using events) could be used to bubble up error messages from each submodule to the main view model.
The quick and very dirty approach:
Grab a reference to mainwindow out of application.current.mainwindow. Cast it to MainWindow. It's a property set to whatever the first window you show is - MainWindow just happens to be the default name of the main window.
You can then set the property on that if it's a public dependency property. Make sure the dp binds twoway in it's metadata.
This is bad because you're referencing ui in your viewmodels and you have no application when you run tests on viewmodels in some test runner.
The quick and dirty approach
Add a public property to app and set this to your instance of mainwindowviewmodel in it's ctor. You can reference app from an piece of code. Add a public property to mainwindowviewmodel and bind to that.
This is bad because you have no application when you run tests on viewmodels in some test runner.
You could add a static with an interface abstracts this away and work round that though.
My suggestion
This hinges on the fact you can use dot notation to bind and that includes
Content.IsBusy on yourcontentcontrol.
You can therefore bind from a parent window to a dependency property of any usercontrol that happens to be in it's contentcontrol.
Add that property using an attached property and bind that to IsBusy in a base viewmodel. Inherit the viewmodels of your child views from that.
One thing to mention is that binding to an attached property is a little odd and rather than just
ElementName=YourContentControl, Path=Content.YourAttachedProperty
You need something like:
ElementName=YourContentControl, Path=Content.(local:AttachClass.YourAttachedProperty)

Binding multiple Caliburn.Micro View Models to different instances of the same view

I am doing some data presentation using CM and WPF, and some of the data tabs have very similar formats but have to be kept in separate VM containing tabs as part of the standard for the application.
My initial thoughts were that I could do this programmatically in the VM by looking for any property pertaining to Views on the VM object (which itself is a Screen object derivation.) Its direct superclass is used as a contract for [ImportMany] so that the parent VM and View can tabulate the collection.
[ImportingConstructor]
public PartiesMasterPartiesViewModel(
IEventAggregator events,
IHelpService help,
ResourceManager<B_Action> actionResource,
IActionService actionService)
: base( events, help, actionResource, actionService)
{
}
protected override void OnActivate()
{
base.OnActivate();
this.Views.Add(new KeyValuePair<object, object>(this,
new PartiesMasterListView()));
}
So either I am not using this property correctly, or it does not do what I thought it does and I need to use another way.
Another way I'm thinking of doing it this explicitly instantiating multiple instances of the same viewmodel and manually adding them to the collection, but this seems like it would be violating what MEF's [ImportMany] would be here to do and weaken the design of the application.
The simplest way to achieve a view shared by multiple view models is to configure the ViewLocator with some extra rules.
In this example I have two view models Examples.ViewModels.SharedData1ViewModel and Examples.ViewModels.SharedData1ViewModel and a single view Examples.Views.SharedDataView that I'd like to be the view that Caliburn.Micro locates for both by default.
In my set up code I can add the following simple regular expression to the ViewLocator.
ViewLocator.NameTransformer.AddRule(
#"^Examples.ViewModels\.SharedData(\d+)ViewModel",
#"Examples.Views.SharedDataView");

MVVM MEF WindowFormHost

I am currently trying to design an application that loads viewmodels through MEF imports.
So far so good, I navigate from viewmodel to viewmodel, having loaded each vm datatemplate through dictionaries.
Each time I navigate, I modify the content of the main contentPresenter in my Shell (MainWindow).
One of the viewmodel allows me to display a WindowFormHost for an activeX control (such as acrobat reader for example). Since WindowFormHost does not allow binding, I created the windowFormHost in the viewmodel and binded it to a ContentPresenter in the view.
And here is where it fails : when coming back to the same viewmodel, the view is created again... throwing a “Element is already the child of another element.” error.
How can I prevent that ? Should I unload WindowFormHost when view is reloaded ? Or Can I keep view instances so that I keep only one instance for each view and let data binding update controls ? (It looks better for memory consumption).
Thanks for your help !
[EDIT]
Loaded dictionary :
<DataTemplate x:Shared="False" DataType="{x:Type vm:DAVPDC3DVIAControlViewModel}">
<vw:MyUserControl />
</DataTemplate>
View :
<DockPanel>
<ContentControl Name="WFH3DVia" Content="{Binding Path=Control3DVIA, Mode=OneWay} </ContentControl>"
<!--<WindowsFormsHost Name="WFH3DVia"></WindowsFormsHost>-->
</DockPanel>
VM (singleton, mef module) :
[Export(typeof(IDAVPDC3DVIAControl))]
public partial class DAVPDC3DVIAControlViewModel : ViewModelBase, IViewModel, IPartImportsSatisfiedNotification
VM (main window)
[Export]
public class MainWindowViewModel : ViewModelBase, IPartImportsSatisfiedNotification
// CurrentUC binds main widow view to controller active viewmodel
public IViewModel CurrentUC
{
get
{
return myAddinManager.CurrentVM;
}
}
Main view :
Controler (displays module on event) :
private void ModuleReadyEventAction(string iModuleName)
{
if (null != this.Modules && this.Modules.Count() > 0)
{
foreach (var item in Modules)
{
IBaseModule ibasemodule = item as IBaseModule;
if (null != ibasemodule)
{
Type tp = ibasemodule.GetType();
if (0 == tp.Name.CompareTo(iModuleName))
{
CurrentVM = ibasemodule.GetViewModel();
break;
}
}
}
}
}
I'm also working on a project in WPF using Prism v4 and MVVM (except I'm using Unity). I also have at least two controls that I need to use which are Windows Forms controls that must be hosted in a WindowsFormsHost. Let me explain my thoughts on the process..
It seems to me, that you are trying to avoid any code in your View's code behind. That's the only reason I can think of that you are moving your WindowsFormsHost into your ViewModel. I think that this is fundamentally the wrong approach. The WindowsFormsHost exists for the reason of displaying a graphical Windows Forms control. Therefore, it belongs in the view!
Now, I understand the appeal of DataBindings. Trust me, I've wanted to able to DataBind many parts of my WindowForms control. Of course, to accept a WPF data binding the property must be a dependency property on a dependency object. The easiest solution, which is not unreasonable, is to simply add the code to configure your windows forms control in the code behind for your view. Adding your UI logic into your ViewModel is an actual violation of the MVVM design pattern, while adding code behind is not. (And in some cases is the best approach)
I've seen possible hacks to try to get around this limitation. Including using "proxies" which inject a databinding, or perhaps extending WindowsFormsHost and adding DependencyProperties which wrap a specific hosted control's properties, or writing classes using reflection and trying to throw in windows forms bindings. However, nothing I've seen can solve the problem completely. For example, my windows forms control can contain other graphical components, and those components would need to support binding as well.
The simplest approach is to simply synchronize your view with your viewmodel in your view's code behind. Your view model can keep the file or document that is open, filename, title, etc., but leave the display and display related controls up to the View.
Last, let me comment more directly on your question. I would need to see how you are registering your View and ViewModel with the MEF Container and how you are navigating to understand why you are receiving that error. It would seem to me that either your view or view model is getting created more than once, while the other is not. Are these registered as singleton types? Regardless, I stand by what I said about not including the WindowsFormsHost in your ViewModel.

WPF: Close a Window from Model MVVM

I am trying to close a window from its ViewModel. I am using the MVVM pattern. I have tired to get the window using;
Window parentWindow = Window.GetWindow(this);
But I cannot do this, how do I get the window of the ViewModel so I am able to close the window. I want to be able to do this in code.
Can you find the parent window in code?
ViewModels should not be referencing the View in any way, including closing windows, in MVVM.
Instead, communication between the View and ViewModel is typically done through some kind of Event or Messaging System, such as Microsoft Prism's EventAggregator, or MVVM Light's Messenger
For example, the View should subscribe to listen for event messages of type CloseWindow, and when it receives one of those message it should close itself. Then the ViewModel simply has to broadcast a CloseWindow message anytime it wants to tell the View to close.
There's a brief overview of event systems in MVVM, and some examples, on my blog post about Communication between ViewModels if you're interested
yes referencing view in viewmodel isn't best practice. WHY? because when you unit test your viewmodel it is require you to instantiate view, for small view will not difficult to do that, but for a complex view with complex tree of dependency? that wont be good.
for me, the easiest way to do communication with view is by passing IInputElement on viewmodel constructor. the bennefit of IInputElement is Routed Event backbone, it has RaiseEvent and AddHandler method required for routed event. thus you can bubble/tunnel/direct event to any view or viewmodel on your application freely without any additional library.
here is my the simplified code on viewmodel but remember this technique only work for view first approach
public class MyViewModel : INotifyPropertyChanged
{
public static readonly RoutedEvent RequestCloseEvent = EventManager.RegisterRoutedEvent("RequestClose",
RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(MyViewModel));
private IInputElement dispatcher;
public MyViewModel(IInputElement dispatcher)
{
this.dispatcher = dispatcher;
}
public void CloseApplication()
{
dispatcher.RaiseEvent(new RoutedEventArgs(RequestCloseEvent));
}
}
on your View simply
DataContext = new MyViewModel(this)
//notice "this" on the constructor
and the root view (Window) of your application simply
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
AddHandler(MyViewModel.RequestCloseEvent, new RoutedEventHandler(onRequestClose));
}
private void onRequestClose(object sender, RoutedEventArgs e)
{
if (MessageBox.Show("Are you sure you want to quit?", "Confirmation", MessageBoxButton.YesNo) == MessageBoxResult.Yes)
{
Close();
}
}
}
and because IInputElement is interface rather than class, you easily create a mock class for your unit test
var target = new MyViewModel(new DispatcherMock)
or you can use mock library like RhinoMocks
for further reading, you can learn more about how to use Routed Event
Let the ViewModel do this, if really in need.
The Models says for example, that there are no longer valid data
pass that information to the ViewModel
the ViewModel recognizes, that it can no longer display anything
and then closes the window.
An empty view is the normal way of expressing that there are no more data
You can define an action in your ViewModel
public Action CloseAction { get; set; }
then, in your window (for example in the DataContextChanged) you can set this action :
((IClosable)viewModel.Content).CloseAction = () => System.Windows.Application.Current.Dispatcher.Invoke(Close());
Well, all this is part of a bigger dependency injection pattern, but basic principle is here...
Next, you juste need to call the action from the VM.
There is a useful behavior for this task which doesn't break MVVM, a Behavior, introduced with Expression Blend 3, to allow the View to hook into commands defined completely within the ViewModel.
This behavior demonstrates a simple technique for allowing the
ViewModel to manage the closing events of the View in a
Model-View-ViewModel application.
This allows you to hook up a behavior in your View (UserControl) which
will provide control over the control's Window, allowing the ViewModel
to control whether the window can be closed via standard ICommands.
Using Behaviors to Allow the ViewModel to Manage View Lifetime in M-V-VM
http://gallery.expression.microsoft.com/WindowCloseBehavior/

WPF MVVM Get Parent from VIEW MODEL

In a MVVM WPF application.
How do you set a second windows parent from the ViewModel?
example:
view1 -- viewModel1
viewModel1's command calls:
var view2 = new view2
view2.Owner = <----This is the problem area. How do I get view1 as the owner here from the viewModel?
view2.Show()
EDIT:
See accepted answer below, then read the following edit.
I'am using MVVM light -> http://mvvmlight.codeplex.com/ (awesome btw)
The baked-in messaging system is great. I am now sending a message from the viewmodel to my view telling it to show another window.
For the message I'am currently using a string with a switch statement in the main view to determine what view to open; however I may tinker with the tokens that also are part of MVVM light toolkit.
Thank you!
In my opinion, opening a new window is the responsibility of the View, not of the ViewModel. Personally, I would use the same approach as used for displaying a dialog box (this was discussed in this forum already):
Have the ViewModel send a Message to the View requesting that it opens a new Window.
(alternatively) use an IDialogService or whatever you want to call it which you pass to the ViewModel's constructor. This service will be in charge of opening the Window (or of delegating this task to the View).
This way, you keep a clean separation of concerns and your VM remains testable (you can unit test that the request to open the new WIndow has been sent, but you couldn't test that the window has been, indeed, open).
Does that make sense?
Cheers,
Laurent
From your viewmodel call
Messenger.Default.Send<NotificationMessage>(new NotificationMessage("Open Window"));
And from your view's codebehind (a view that call the second
view) easily write this in the constructor:
Messenger.Default.Register<NotificationMessage>(this, ReplyToMessage);
And also write this method in the view's codebehind:
private void ReplyToMessage(NotificationMessage msg)
{
if (msg.Notification == "Open Window")
{
SecondWindow win = new SecondWindow();
win.ShowDialog();
}
}
I don't have an answer of my own but here's a few links to things I've been looking at lately that might help. I'll also be interested in anything others suggest.
As I understand it, the key thing is, you shouldn't be creating Views from within a View Model if possible, so you need a means of communicating what you need in a loosely coupled fashion.
http://www.codeproject.com/KB/WPF/XAMLDialog.aspx
http://www.codeproject.com/KB/architecture/MVVM_Dialogs.aspx
Handling Dialogs in WPF with MVVM
You can do in this way like you need to create some events and register those in view and call these in view model.and open that pop up window.
Like This example
public class Mainclass : MainView
{
public delegate abc RegisterPopUp(abc A);
public RegisterPopUp POpUpEvent;
public RelayCommand ShowCommand { private set; get; }
public void ShowCommand()
{
ShowCommand("Your parameter");
}
}
inside the view
MainView mn = new MainView();
Register the event here like mn.POpUpEvent += then click on tab button double time and in registers popup method write the code for opening the pop up window.
Prism-Event Aggrigator is good approach, where we can create independent module without dependency. first viewmodel will publish event and then another view or view or viewmodel can subscribe that event from event aggrigator.
in this case Unity container can also use to inject one viewmodel in to another with dependency injection.

Categories