MVVM: Is code-behind evil or just pragmatic? - c#

Imagine you want a Save & Close and a Cancel & Close button on your fancy WPF MVVM window?
How would you go about it? MVVM dictates that you bind the button to an ICommand and inversion of control dictates that your View may know your ViewModel but not the other way around.
Poking around the net I found a solution that has a ViewModel closing event to which the View subscribes to like this:
private void OnLoaded(Object sender
, RoutedEventArgs e)
{
IFilterViewModel viewModel = (IFilterViewModel)DataContext;
viewModel.Closing += OnViewModelClosing;
}
private void OnViewModelClosing(Object sender
, EventArgs<Result> e)
{
IFilterViewModel viewModel = (IFilterViewModel)DataContext;
viewModel.Closing -= OnViewModelClosing;
DialogResult = (e.Value == Result.OK) ? true : false;
Close();
}
But that is code-behind mixed in with my so far very well designed MVVM.
Another problem would be showing a licensing problem message box upon showing the main window. Again I could use the Window.Loaded event like I did above, but that's also breaking MVVM, is it not?
Is there a clean way or should one be pragmatical instead of pedantic in these cases?

First, create an interface that contains only the Close method:
interface IClosable
{
void Close();
}
Next, make your window implement IClosable:
class MyWindow : Window, IClosable
{
public MyWindow()
{
InitializeComponent();
}
}
Then let the view pass itself as IClosable as command parameter to the view model:
<Button Command="{Binding CloseCommand}" CommandParameter="{Binding RelativeSource={RelativeSource AncestorType={x:Type Window}}}" />
And lastly, the command calls Close:
CloseCommand = new DelegateCommand<IClosable>( view => view.Close() );
And what have we now?
we have a button that closes the window
we have no code in code-behind except , IClosable
the view model knows nothing about the view, it just gets an arbitrary object that can be closed
the command can easily be unit tested

There is nothing wrong or right with using code behind, this is mainly opinion based and depends on your preference.
This example shows how to close a window using an MVVM design pattern without code behind.
<Button Name="btnLogin" IsDefault="True" Content="Login" Command="{Binding ShowLoginCommand}" CommandParameter="{Binding ElementName=LoginWindow}"/>
<!-- the CommandParameter should bind to your window, either by name or relative or what way you choose, this will allow you to hold the window object and call window.Close() -->
basically you pass the window as a parameter to the command. IMO your viewmodel shouldn't be aware of the control, so this version is not that good. I would pass a Func<object>/ some interface to the viewmodel for closing the window using dependency injection.

Take a look at some toolkits e.g. MVVMLight has EventToCommand, which allows you to bind command to events. I generally try my best to limit logic in View, as it's harder to test it.
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
xmlns:command="http://www.galasoft.ch/mvvmlight"
...
<i:Interaction.Triggers>
<i:EventTrigger EventName="Loaded">
<command:EventToCommand Command="{Binding YourCommandInVM}"/>
</i:EventTrigger>
</i:Interaction.Triggers>

Sometimes I use a work-around.
Assumes u have a view "MainWindow" and a viewmodel "MainWindowVM".
public class MainWindowVM
{
private MainWindow mainWindow;
public delegate void EventWithoudArg();
public event EventWithoudArg Closed;
public MainWindowVM()
{
mainWindow = new MainWindow();
mainWindow.Closed += MainWindow_Closed;
mainWindow.DataContext = this;
mainWindow.Loaded += MainWindow_Loaded;
mainWindow.Closing += MainWindow_Closing;
mainWindow.Show();
}
private void MainWindow_Loaded()
{
//your code
}
private void MainWindow_Closing(object sender, System.ComponentModel.CancelEventArgs e)
{
//your code
}
private void MainWindow_Closed()
{
Closed?.Invoke();
}
}
Here I store my view in a private variable so you can access it if you need it. It breaks a bit the MVVM.
In my viewmodel, I create a new view and show it.
Here I also capture the closing event of the view an pass it to an own event.
You can also add a method to the .Loaded and .Closing events of your view.
In App.xaml.cs you just have to create a new viewmodel object.
public partial class App : Application
{
public App()
{
MainWindowVM mainWindowVM = new MainWindowVM();
mainWindowVM.Closed += Mwvm_Close;
}
private void Mwvm_Close()
{
this.Shutdown();
}
}
I create a new viewmodel object and capture it own close-event and bind it to the shutdown method of the App.

Your description indicates that the view model is some kind of a document view. If that's correct then I would leave Save, Close, etc. to be handled by the document container e.g. the application or the main window, because these commands are on a level above the document in the same way as copy/paste are on the application level. In fact ApplicationCommands has predefined commands for both Save and Close which indicates a certain approach from the authors of the framework.

Related

Accessing Xamarin.Forms inside ViewModel

I am very new to Cross platform development in Xamarin.forms. My code should be highly unit testable and I have created a ViewModel class to communicate between my View and the Model. I want to know, If we are going to access Xamarin.forms (using Xamarin.Forms;), inside the viewmodel, is it a bad practice or violating MVVM concept. If so How we can use Command inside ViewModel to bind with the view.
Thanks
Accessing the view from the view model is indeed "against" the MVVM principle. So, you're right on that! Binding a Command though, isn't that hard and works just as any other binding that you might've used.
In your view model, just define a property for the Command:
public class YourViewModel
{
public Command YourCommand { get; set; }
}
Then in the constructor define the logic that should be executed:
public YourViewModel()
{
YourCommand = new Command(() =>
{
Console.WriteLine("TADA!");
});
}
Of course, you could define it is it's own separate method or declare it another way. For brevity, I have done it inline for now.
To bind to it from XAML, simply do:
<Button Text="Make it so!" Command="{Binding YourCommand}" />
and make sure that you have set the BindingContext property on your page to the view model. If you are using an MVVM framework you might have already done so.
At some point, you will probably want to bind to a cell in a ListView or similar. In that case, keep in mind that the scope changes. You are then binding to the view model behind that cell and not the page. So, you will have to make a reference to the page to reach that BindingContext. But cross that bridge when you get there, just keep in mind that there is something special about it.
Use below code, so you don't need to import Xamarin.Forms in your ViewModel:
ButtonBehavior
public class ButtonBehavior : Behavior<Button>
{
protected override void OnAttachedTo(Button bindable)
{
base.OnAttachedTo(bindable);
bindable.Clicked += Bindable_Clicked;
}
private void Bindable_Clicked(object sender, EventArgs e)
{
//Use you logic here
}
protected override void OnDetachingFrom(Button bindable)
{
base.OnDetachingFrom(bindable);
bindable.Clicked -= Bindable_Clicked;
}
}
View
<Button Text="Click Me" HeightRequest="50" WidthRequest="80">
<Button.Behaviors>
<behavior:ButtonBehavior/>
</Button.Behaviors>
</Button>

Fire command from custom canvas

I develop application with MVVM pattern. In my viewmodel project I have custom Command. In View I have custom canvas. I want to fire command from my canvas code behind but I dont have idea how.
My app startup (startup is in other project):
private void ApplicationStartup(object sender, StartupEventArgs e)
{
// init main ViewModel
var viewModel = new MainViewModel(Container);
// init and show window
var mainWindow = new MainWindow();
mainWindow.Show();
mainWindow.DataContext = viewModel;
}
I dont want to have reference to ViewModel in View, because I need reference to View in ViewModel.
Thanks!
The way you usually do this is implement your own COMMAND in the CustomCanvas class. After that, you bind that command against command handler in viewmodel.
Just like you do with button:
<Button
Command="{Binding YourVmCommand}"
/>
Makes sense?
To implement such functionality, you need to hack through ButtonBase.cs and see how it's done.
There's a lot code involved, but the idea is very simple.
1) Create new property in your View.
public ICommand Command { get; set; }
2) Bind to it in XAML
3) Execute it in View, when you need.
if(Command != null && Command.CanExecute(parameter))
Command.Execute(parameter);

Triggering RelayCommand from code-behind

I have a Main Page for my application that is formed froma number of other windows. One of which is the settings for my applications and is open/closed by clicking a button from my Main Page. This window has a View Model as well as two buttons, Save and Cancel. Pressing Cancel is used to restore the previous settings and Save just stores them. Now, when I use the main menu to close the Properties I want the Cancel to be called and am wondering about the best way to do this.
So in my view model I have something like this:
public RelayCommand CancelRC { get; private set; }
public PropertiesViewModel
{
CancelRC = new RelayCommand(RestoreProperties)
}
private RestoreProperties
{
// Restore
}
In my MainPage.xaml.cs
private void Properties_Click(object sender, RoutedEventArgs e)
{
if (PropertiesForm.Visibility == Visibility.Collapsed)
{
PropertiesForm.Visibility = Visibility.Visible;
PropertiesForm.IsSelected = true;
}
else
{
PropertiesForm.Visibility = Visibility.Collapsed;
IncidentForm.IsSelected = true;
// Send a Cancel relaycommand to the Properties View Model?
}
}
The obvious solution to me is to trigger the RelayCommand manually but I am not sure this is the most appropriate way to do it and I am not sure how you would trigger that anyway. So it this the way to do it or is there a more preferred way to do something like this?
Similarly is manually hiding and showing the Properties via the Main Page like this the best way to do it?
Remove the Properties_Click method and in your xaml do the following:
<Button Command="{Binding CancelRC}">Properties</Button>
This will cause the button to use RelayCommand.CanExecute and RelayCommand.Execute with no code on your part. The code assumes the window datacontext is set to your View Model.

How to determine which View executed a command

I have a View called InformationView.xaml and this same View is re-used to display the same information from 3 different sources (each view has a different window title). Each View has their datacontext set the same instance of one ViewModel type. Within my ViewModel class, I have an ICommand property that the 'Close' button inside the View is bound to. -- Is there a way to determine who the sender was of the command? (specifically, by window title).
Here is an example:
I have a view class with the following button ("Note: each View will have a different window title / display data from a different source--but the same View is used)
<Button Width="75" Height="23" Margin="0,0,5,5" Content="Close" Command="{Binding CloseCommand}" />
I have a ViewModel class with the following command
public ICommand CloseCommand
{
get
{
if (this._closeCommand == null)
{
this._closeCommand = new RelayCommand(Command => this.OnClose());
}
return _closeCommand;
}
}
I am looking for a way to determine which window executed the command (I will have multiple instances of the View using the same ViewModel).
I'm not sure if I understand you correctly. However, you could possibly implement the Unloaded event. Set a breakpoint inside that event method and when you hit the breakpoint. You could check the window title property for that view.
What about just making the Close() method public so that other objects can specify what the close behavior should be?
Something along the lines of this in your InformationViewModel:
public event EventHandler RequestClose;
void OnRequestClose()
{
EventHandler handler = this.RequestClose;
if (handler != null)
handler(this, EventArgs.Empty);
}
Then you can use it from within your other view models like this:
InformationViewModel.Close += CloseMethod;
public CloseMethod(object sender, EventArgs e)
{
// Implement close logic here
}

How should the ViewModel close the form?

I'm trying to learn WPF and the MVVM problem, but have hit a snag.
This question is similar but not quite the same as this one (handling-dialogs-in-wpf-with-mvvm)...
I have a "Login" form written using the MVVM pattern.
This form has a ViewModel which holds the Username and Password, which are bound to the view in the XAML using normal data bindings.
It also has a "Login" command which is bound to the "Login" button on the form, agan using normal databinding.
When the "Login" command fires, it invokes a function in the ViewModel which goes off and sends data over the network to log in. When this function completes, there are 2 actions:
The login was invalid - we just show a MessageBox and all is fine
The login was valid, we need to close the Login form and have it return true as its DialogResult...
The problem is, the ViewModel knows nothing about the actual view, so how can it close the view and tell it to return a particular DialogResult?? I could stick some code in the CodeBehind, and/or pass the View through to the ViewModel, but that seems like it would defeat the whole point of MVVM entirely...
Update
In the end I just violated the "purity" of the MVVM pattern and had the View publish a Closed event, and expose a Close method. The ViewModel would then just call view.Close. The view is only known via an interface and wired up via an IOC container, so no testability or maintainability is lost.
It seems rather silly that the accepted answer is at -5 votes! While I'm well aware of the good feelings that one gets by solving a problem while being "pure", Surely I'm not the only one that thinks that 200 lines of events, commands and behaviors just to avoid a one line method in the name of "patterns" and "purity" is a bit ridiculous....
I was inspired by Thejuan's answer to write a simpler attached property. No styles, no triggers; instead, you can just do this:
<Window ...
xmlns:xc="clr-namespace:ExCastle.Wpf"
xc:DialogCloser.DialogResult="{Binding DialogResult}">
This is almost as clean as if the WPF team had gotten it right and made DialogResult a dependency property in the first place. Just put a bool? DialogResult property on your ViewModel and implement INotifyPropertyChanged, and voilĂ , your ViewModel can close the Window (and set its DialogResult) just by setting a property. MVVM as it should be.
Here's the code for DialogCloser:
using System.Windows;
namespace ExCastle.Wpf
{
public static class DialogCloser
{
public static readonly DependencyProperty DialogResultProperty =
DependencyProperty.RegisterAttached(
"DialogResult",
typeof(bool?),
typeof(DialogCloser),
new PropertyMetadata(DialogResultChanged));
private static void DialogResultChanged(
DependencyObject d,
DependencyPropertyChangedEventArgs e)
{
var window = d as Window;
if (window != null)
window.DialogResult = e.NewValue as bool?;
}
public static void SetDialogResult(Window target, bool? value)
{
target.SetValue(DialogResultProperty, value);
}
}
}
I've also posted this on my blog.
From my perspective the question is pretty good as the same approach would be used not only for the "Login" window, but for any kind of window. I've reviewed a lot of suggestions and none are OK for me. Please review my suggestion that was taken from the MVVM design pattern article.
Each ViewModel class should inherit from WorkspaceViewModel that has the RequestClose event and CloseCommand property of the ICommand type. The default implementation of the CloseCommand property will raise the RequestClose event.
In order to get the window closed, the OnLoaded method of your window should be overridden:
void CustomerWindow_Loaded(object sender, RoutedEventArgs e)
{
CustomerViewModel customer = CustomerViewModel.GetYourCustomer();
DataContext = customer;
customer.RequestClose += () => { Close(); };
}
or OnStartup method of you app:
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
MainWindow window = new MainWindow();
var viewModel = new MainWindowViewModel();
viewModel.RequestClose += window.Close;
window.DataContext = viewModel;
window.Show();
}
I guess that RequestClose event and CloseCommand property implementation in the WorkspaceViewModel are pretty clear, but I will show them to be consistent:
public abstract class WorkspaceViewModel : ViewModelBase
// There's nothing interesting in ViewModelBase as it only implements the INotifyPropertyChanged interface
{
RelayCommand _closeCommand;
public ICommand CloseCommand
{
get
{
if (_closeCommand == null)
{
_closeCommand = new RelayCommand(
param => Close(),
param => CanClose()
);
}
return _closeCommand;
}
}
public event Action RequestClose;
public virtual void Close()
{
if ( RequestClose != null )
{
RequestClose();
}
}
public virtual bool CanClose()
{
return true;
}
}
And the source code of the RelayCommand:
public class RelayCommand : ICommand
{
#region Constructors
public RelayCommand(Action<object> execute, Predicate<object> canExecute)
{
if (execute == null)
throw new ArgumentNullException("execute");
_execute = execute;
_canExecute = canExecute;
}
#endregion // Constructors
#region ICommand Members
[DebuggerStepThrough]
public bool CanExecute(object parameter)
{
return _canExecute == null ? true : _canExecute(parameter);
}
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
public void Execute(object parameter)
{
_execute(parameter);
}
#endregion // ICommand Members
#region Fields
readonly Action<object> _execute;
readonly Predicate<object> _canExecute;
#endregion // Fields
}
P.S. Don't treat me badly for those sources! If I had them yesterday that would have saved me a few hours...
P.P.S. Any comments or suggestions are welcome.
There are a lot of comments arguing the pros and cons of MVVM here. For me, I agree with Nir; it's a matter of using the pattern appropriately and MVVM doesn't always fit. People seems to have become willing to sacrifice all of the most important principles of software design JUST to get it to fit MVVM.
That said,..i think your case could be a good fit with a bit of refactoring.
In most cases I've come across, WPF enables you to get by WITHOUT multiple Windows. Maybe you could try using Frames and Pages instead of Windows with DialogResults.
In your case my suggestion would be have LoginFormViewModel handle the LoginCommand and if the login is invalid, set a property on LoginFormViewModel to an appropriate value (false or some enum value like UserAuthenticationStates.FailedAuthentication). You'd do the same for a successful login (true or some other enum value). You'd then use a DataTrigger which responds to the various user authentication states and could use a simple Setter to change the Source property of the Frame.
Having your login Window return a DialogResult i think is where you're getting confused; that DialogResult is really a property of your ViewModel. In my, admittedly limited experience with WPF, when something doesn't feel right it usually because I'm thinking in terms of how i would've done the same thing in WinForms.
Hope that helps.
Assuming your login dialog is the first window that gets created, try this inside your LoginViewModel class:
void OnLoginResponse(bool loginSucceded)
{
if (loginSucceded)
{
Window1 window = new Window1() { DataContext = new MainWindowViewModel() };
window.Show();
App.Current.MainWindow.Close();
App.Current.MainWindow = window;
}
else
{
LoginError = true;
}
}
This is a simple and clean solution - You add an event to the ViewModel and instruct the Window to close itself when that event is fired.
For more details see my blog post, Close window from ViewModel.
XAML:
<Window
x:Name="this"
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
xmlns:ei="http://schemas.microsoft.com/expression/2010/interactions">
<i:Interaction.Triggers>
<i:EventTrigger SourceObject="{Binding}" EventName="Closed">
<ei:CallMethodAction
TargetObject="{Binding ElementName=this}"
MethodName="Close"/>
</i:EventTrigger>
</i:Interaction.Triggers>
<Window>
ViewModel:
private ICommand _SaveAndCloseCommand;
public ICommand SaveAndCloseCommand
{
get
{
return _SaveAndCloseCommand ??
(_SaveAndCloseCommand = new DelegateCommand(SaveAndClose));
}
}
private void SaveAndClose()
{
Save();
Close();
}
public event EventHandler Closed;
private void Close()
{
if (Closed != null) Closed(this, EventArgs.Empty);
}
Note: The example uses Prism's DelegateCommand (see Prism: Commanding), but any ICommand implementation can be used for that matter.
You can use behaviors from this official package.
The way I would handle it is to add an event handler in my ViewModel. When the user was successfully logged in I would fire the event. In my View I would attach to this event and when it fired I would close the window.
Here's what I initially did, which does work, however it seems rather long-winded and ugly (global static anything is never good)
1: App.xaml.cs
public partial class App : Application
{
// create a new global custom WPF Command
public static readonly RoutedUICommand LoggedIn = new RoutedUICommand();
}
2: LoginForm.xaml
// bind the global command to a local eventhandler
<CommandBinding Command="client:App.LoggedIn" Executed="OnLoggedIn" />
3: LoginForm.xaml.cs
// implement the local eventhandler in codebehind
private void OnLoggedIn( object sender, ExecutedRoutedEventArgs e )
{
DialogResult = true;
Close();
}
4: LoginFormViewModel.cs
// fire the global command from the viewmodel
private void OnRemoteServerReturnedSuccess()
{
App.LoggedIn.Execute(this, null);
}
I later on then removed all this code, and just had the LoginFormViewModel call the Close method on it's view. It ended up being much nicer and easier to follow. IMHO the point of patterns is to give people an easier way to understand what your app is doing, and in this case, MVVM was making it far harder to understand than if I hadn't used it, and was now an anti-pattern.
Ok, so this question is nearly 6 years old and I still can't find in here what I think it's the proper answer, so allow me to share my "2 cents"...
I actually have 2 ways of doing it, first one is the simple one...the second on the right one, so if you are looking for the right one, just skip #1 and jump to #2:
1. Quick and Easy (but not complete)
If I have just a small project I sometimes just create a CloseWindowAction in the ViewModel:
public Action CloseWindow { get; set; } // In MyViewModel.cs
And whoever crates the View, or in the View's code behind I just set the Method the Action will call:
(remember MVVM is about separation of the View and the ViewModel...the View's code behins is still the View and as long as there is proper separation you are not violating the pattern)
If some ViewModel creates a new window:
private void CreateNewView()
{
MyView window = new MyView();
window.DataContext = new MyViewModel
{
CloseWindow = window.Close,
};
window.ShowDialog();
}
Or if you want it in your Main Window, just place it under your View's constructor:
public MyView()
{
InitializeComponent();
this.DataContext = new MainViewModel
{
CloseWindow = this.Close
};
}
when you want to close the window, just call the Action on your ViewModel.
2. The right way
Now the proper way of doing it is using Prism (IMHO), and all about it can be found here.
You can make an Interaction Request, populate it with whatever data you will need in your new Window, lunch it, close it and even receive data back. All of this encapsulated and MVVM approved. You even get a status of how the Window was closed, like if the User Canceled or Accepted (OK button) the Window and data back if you need it. It's a bit more complicated and Answer #1, but it's a lot more complete, and a Recommended Pattern by Microsoft.
The link I gave have all the code snippets and examples, so I won't bother to place any code in here, just read the article of download the Prism Quick Start and run it, it's really simple to understad just a little more verbose to make it work, but the benefits are bigger than just closing a window.
public partial class MyWindow: Window
{
public ApplicationSelection()
{
InitializeComponent();
MyViewModel viewModel = new MyViewModel();
DataContext = viewModel;
viewModel.RequestClose += () => { Close(); };
}
}
public class MyViewModel
{
//...Your code...
public event Action RequestClose;
public virtual void Close()
{
if (RequestClose != null)
{
RequestClose();
}
}
public void SomeFunction()
{
//...Do something...
Close();
}
}
FYI, I ran into this same problem and I think I figured out a work around that doesn't require globals or statics, although it may not be the best answer. I let the you guys decide that for yourself.
In my case, the ViewModel that instantiates the Window to be displayed (lets call it ViewModelMain) also knows about the LoginFormViewModel (using the situation above as an example).
So what I did was to create a property on the LoginFormViewModel that was of type ICommand (Lets call it CloseWindowCommand). Then, before I call .ShowDialog() on the Window, I set the CloseWindowCommand property on the LoginFormViewModel to the window.Close() method of the Window I instantiated. Then inside the LoginFormViewModel all I have to do is call CloseWindowCommand.Execute() to close the window.
It is a bit of a workaround/hack I suppose, but it works well without really breaking the MVVM pattern.
Feel free to critique this process as much as you like, I can take it! :)
This is probably very late, but I came across the same problem and I found a solution that works for me.
I can't figure out how to create an app without dialogs(maybe it's just a mind block). So I was at an impasse with MVVM and showing a dialog. So I came across this CodeProject article:
http://www.codeproject.com/KB/WPF/XAMLDialog.aspx
Which is a UserControl that basically allows a window to be within the visual tree of another window(not allowed in xaml). It also exposes a boolean DependencyProperty called IsShowing.
You can set a style like,typically in a resourcedictionary, that basically displays the dialog whenever the Content property of the control != null via triggers:
<Style TargetType="{x:Type d:Dialog}">
<Style.Triggers>
<Trigger Property="HasContent" Value="True">
<Setter Property="Showing" Value="True" />
</Trigger>
</Style.Triggers>
</Style>
In the view where you want to display the dialog simply have this:
<d:Dialog Content="{Binding Path=DialogViewModel}"/>
And in your ViewModel all you have to do is set the property to a value(Note: the ViewModel class must support INotifyPropertyChanged for the view to know something happened ).
like so:
DialogViewModel = new DisplayViewModel();
To match the ViewModel with the View you should have something like this in a resourcedictionary:
<DataTemplate DataType="{x:Type vm:DisplayViewModel}">
<vw:DisplayView/>
</DataTemplate>
With all of that you get a one-liner code to show dialog. The problem you get is you can't really close the dialog with just the above code. So that's why you have to put in an event in a ViewModel base class which DisplayViewModel inherits from and instead of the code above, write this
var vm = new DisplayViewModel();
vm.RequestClose += new RequestCloseHandler(DisplayViewModel_RequestClose);
DialogViewModel = vm;
Then you can handle the result of the dialog via the callback.
This may seem a little complex, but once the groundwork is laid, it's pretty straightforward. Again this is my implementation, I'm sure there are others :)
Hope this helps, it saved me.
You could have the ViewModel expose an event that the View registers to. Then, when the ViewModel decides its time to close the view, it fires that event which causes the view to close. If you want a specific result value passed back, then you would have a property in the ViewModel for that.
Why not just pass the window as a command parameter?
C#:
private void Cancel( Window window )
{
window.Close();
}
private ICommand _cancelCommand;
public ICommand CancelCommand
{
get
{
return _cancelCommand ?? ( _cancelCommand = new Command.RelayCommand<Window>(
( window ) => Cancel( window ),
( window ) => ( true ) ) );
}
}
XAML:
<Window x:Class="WPFRunApp.MainWindow"
x:Name="_runWindow"
...
<Button Content="Cancel"
Command="{Binding Path=CancelCommand}"
CommandParameter="{Binding ElementName=_runWindow}" />
Just to add to the massive number of answers, I want to add the following. Assuming that you have a ICommand on your ViewModel, and you want that command to close its window (or any other action for that matter), you can use something like the following.
var windows = Application.Current.Windows;
for (var i=0;i< windows.Count;i++ )
if (windows[i].DataContext == this)
windows[i].Close();
It's not perfect, and might be difficult to test (as it is hard to mock/stub a static) but it is cleaner (IMHO) than the other solutions.
Erick
I implemented Joe White's solution, but ran into problems with occasional "DialogResult can be set only after Window is created and shown as dialog" errors.
I was keeping the ViewModel around after the View was closed and occasionally I later opened a new View using the same VM. It appears that closing the new View before the old View had been garbage collected resulted in DialogResultChanged trying to set the DialogResult property on the closed window, thus provoking the error.
My solution was to change DialogResultChanged to check the window's IsLoaded property:
private static void DialogResultChanged(
DependencyObject d,
DependencyPropertyChangedEventArgs e)
{
var window = d as Window;
if (window != null && window.IsLoaded)
window.DialogResult = e.NewValue as bool?;
}
After making this change any attachments to closed dialogs are ignored.
I ended up blending Joe White's answer and some code from Adam Mills's answer, since I needed to show a user control in a programmatically created window. So the DialogCloser need not be on the window, it can be on the user control itself
<UserControl ...
xmlns:xw="clr-namespace:Wpf"
xw:DialogCloser.DialogResult="{Binding DialogResult}">
And the DialogCloser will find the window of the user control if it was not attached to the window itself.
namespace Wpf
{
public static class DialogCloser
{
public static readonly DependencyProperty DialogResultProperty =
DependencyProperty.RegisterAttached(
"DialogResult",
typeof(bool?),
typeof(DialogCloser),
new PropertyMetadata(DialogResultChanged));
private static void DialogResultChanged(
DependencyObject d,
DependencyPropertyChangedEventArgs e)
{
var window = d.GetWindow();
if (window != null)
window.DialogResult = e.NewValue as bool?;
}
public static void SetDialogResult(DependencyObject target, bool? value)
{
target.SetValue(DialogResultProperty, value);
}
}
public static class Extensions
{
public static Window GetWindow(this DependencyObject sender_)
{
Window window = sender_ as Window;
return window ?? Window.GetWindow( sender_ );
}
}
}
Behavior is the most convenient way here.
From one hand, it can be binded to the given viewmodel (that can
signal "close the form!")
From another hand, it has access to the form itself so can subscribe to necessary form-specific events, or show confirmation dialog, or anything else.
Writing necessary behavior can be seen boring very first time. However, from now on, you can reuse it on every single form you need by exact one-liner XAML snippet. And if necessary, you can extract it as a separate assembly so it can be included into any next project you want.
Another solution is to create property with INotifyPropertyChanged in View Model like DialogResult, and then in Code Behind write this:
public class SomeWindow: ChildWindow
{
private SomeViewModel _someViewModel;
public SomeWindow()
{
InitializeComponent();
this.Loaded += SomeWindow_Loaded;
this.Closed += SomeWindow_Closed;
}
void SomeWindow_Loaded(object sender, RoutedEventArgs e)
{
_someViewModel = this.DataContext as SomeViewModel;
_someViewModel.PropertyChanged += _someViewModel_PropertyChanged;
}
void SomeWindow_Closed(object sender, System.EventArgs e)
{
_someViewModel.PropertyChanged -= _someViewModel_PropertyChanged;
this.Loaded -= SomeWindow_Loaded;
this.Closed -= SomeWindow_Closed;
}
void _someViewModel_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == SomeViewModel.DialogResultPropertyName)
{
this.DialogResult = _someViewModel.DialogResult;
}
}
}
The most important fragment is _someViewModel_PropertyChanged.
DialogResultPropertyName can be some public const string in SomeViewModel.
I use this kind of trick to make some changes in View Controls in case when this is hard to do in ViewModel. OnPropertyChanged in ViewModel you can do anything you want in View. ViewModel is still 'unit testable' and some small lines of code in code behind makes no difference.
I would go this way:
using GalaSoft.MvvmLight;
using GalaSoft.MvvmLight.Command;
using GalaSoft.MvvmLight.Messaging;
// View
public partial class TestCloseWindow : Window
{
public TestCloseWindow() {
InitializeComponent();
Messenger.Default.Register<CloseWindowMsg>(this, (msg) => Close());
}
}
// View Model
public class MainViewModel: ViewModelBase
{
ICommand _closeChildWindowCommand;
public ICommand CloseChildWindowCommand {
get {
return _closeChildWindowCommand?? (_closeChildWindowCommand = new RelayCommand(() => {
Messenger.Default.Send(new CloseWindowMsg());
}));
}
}
}
public class CloseWindowMsg
{
}
I've read all the answers but I must say, most of them are just not good enough or even worse.
You could handle this beatifully with DialogService class which responsibility is to show dialog window and return dialog result. I have create sample project demonstrating it's implementation and usage.
here are most important parts:
//we will call this interface in our viewmodels
public interface IDialogService
{
bool? ShowDialog(object dialogViewModel, string caption);
}
//we need to display logindialog from mainwindow
public class MainWindowViewModel : ViewModelBase
{
public string Message {get; set;}
public void ShowLoginCommandExecute()
{
var loginViewModel = new LoginViewModel();
var dialogResult = this.DialogService.ShowDialog(loginViewModel, "Please, log in");
//after dialog is closed, do someting
if (dialogResult == true && loginViewModel.IsLoginSuccessful)
{
this.Message = string.Format("Hello, {0}!", loginViewModel.Username);
}
}
}
public class DialogService : IDialogService
{
public bool? ShowDialog(object dialogViewModel, string caption)
{
var contentView = ViewLocator.GetView(dialogViewModel);
var dlg = new DialogWindow
{
Title = caption
};
dlg.PART_ContentControl.Content = contentView;
return dlg.ShowDialog();
}
}
Isn't this just simpler? more straitforward, more readable and last but not least easier to debug than EventAggregator or other similar solutions?
as you can see, In my view models I'm have used ViewModel first approach described in my post here: Best practice for calling View from ViewModel in WPF
Of course, in real world, the DialogService.ShowDialog must have more option to configure the dialog, e.g. buttons and commands they should execute. There are different way of doing so, but its out of scope :)
While this doesn't answer the question of how to do this via the viewmodel, this does show how to do it using only XAML + the blend SDK.
I chose to download and use two files from the Blend SDK, both of which you can as a package from Microsoft through NuGet. The files are:
System.Windows.Interactivity.dll and Microsoft.Expression.Interactions.dll
Microsoft.Expression.Interactions.dll gives you nice capabilities such as the ability to set property or invoke a method on your viewmodel or other target and has other widgets inside as well.
Some XAML:
<Window x:Class="Blah.Blah.MyWindow"
...
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
xmlns:ei="http://schemas.microsoft.com/expression/2010/interactions"
...>
<StackPanel>
<Button x:Name="OKButton" Content="OK">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Click">
<ei:ChangePropertyAction
TargetObject="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}}"
PropertyName="DialogResult"
Value="True"
IsEnabled="{Binding SomeBoolOnTheVM}" />
</i:EventTrigger>
</Button>
<Button x:Name="CancelButton" Content="Cancel">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Click">
<ei:ChangePropertyAction
TargetObject="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}}"
PropertyName="DialogResult"
Value="False" />
</i:EventTrigger>
</Button>
<Button x:Name="CloseButton" Content="Close">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Click">
<!-- method being invoked should be void w/ no args -->
<ei:CallMethodAction
TargetObject="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}}"
MethodName="Close" />
</i:EventTrigger>
</i:Interaction.Triggers>
</Button>
<StackPanel>
</Window>
Note that if you're just going for simple OK/Cancel behavior, you can get away w/ using the IsDefault and IsCancel properties as long as the window is shown w/ Window.ShowDialog().
I personally had problems w/ a button that had the IsDefault property set to true, but it was hidden when the page is loaded. It didn't seem to want to play nicely after it was shown, so I just am setting the Window.DialogResult property as shown above instead and it works for me.
Here is the simple bug free solution (with source code), It is working for me.
Derive your ViewModel from INotifyPropertyChanged
Create a observable property CloseDialog in ViewModel
public void Execute()
{
// Do your task here
// if task successful, assign true to CloseDialog
CloseDialog = true;
}
private bool _closeDialog;
public bool CloseDialog
{
get { return _closeDialog; }
set { _closeDialog = value; OnPropertyChanged(); }
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged([CallerMemberName]string property = "")
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(property));
}
}
}
Attach a Handler in View for this property change
_loginDialogViewModel = new LoginDialogViewModel();
loginPanel.DataContext = _loginDialogViewModel;
_loginDialogViewModel.PropertyChanged += OnPropertyChanged;
Now you are almost done. In the event handler make DialogResult = true
protected void OnPropertyChanged(object sender, PropertyChangedEventArgs args)
{
if (args.PropertyName == "CloseDialog")
{
DialogResult = true;
}
}
Create a Dependency Property in your View/any UserControl(or Window you want to close). Like below:
public bool CloseTrigger
{
get { return (bool)GetValue(CloseTriggerProperty); }
set { SetValue(CloseTriggerProperty, value); }
}
public static readonly DependencyProperty CloseTriggerProperty =
DependencyProperty.Register("CloseTrigger", typeof(bool), typeof(ControlEventBase), new PropertyMetadata(new PropertyChangedCallback(OnCloseTriggerChanged)));
private static void OnCloseTriggerChanged(DependencyObject dp, DependencyPropertyChangedEventArgs e)
{
//write Window Exit Code
}
And bind it from your ViewModel's property:
<Window x:Class="WpfStackOverflowTempProject.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Width="525"
CloseTrigger="{Binding Path=CloseWindow,Mode=TwoWay}"
Property In VeiwModel:
private bool closeWindow;
public bool CloseWindow
{
get { return closeWindow; }
set
{
closeWindow = value;
RaiseChane("CloseWindow");
}
}
Now trigger the close operation by changing the CloseWindow value in ViewModel. :)
Where you need to close the window, simply put this in the viewmodel:
ta-da
foreach (Window window in Application.Current.Windows)
{
if (window.DataContext == this)
{
window.Close();
return;
}
}
Application.Current.MainWindow.Close()
Thats enough!

Categories

Resources