How to determine which View executed a command - c#

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
}

Related

MVVM: Is code-behind evil or just pragmatic?

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.

Calling multiple OnPropertyChanged within the same Setter (or is there a other easy way)

I am trying to keep my question simple and to the point.
At the moment, if I have a property that updates the underlying Model data, and it therefore needs to inform a few other properties that the source has changed, I do it like this:
public Data.MeetingInfo.Meeting Meeting
{
get { return _Meeting; }
set
{
if(value != null)
{
_Meeting = value;
if (_Meeting.IsDirty)
{
_Model.Serialize();
_Meeting.MarkClean();
OnPropertyChanged("Meeting");
OnPropertyChanged("BibleReadingMain");
OnPropertyChanged("BibleReadingClass1");
OnPropertyChanged("BibleReadingClass2");
}
}
}
}
private Data.MeetingInfo.Meeting _Meeting;
As you can see, I added several different OnPropertyChanged method calls. Is this an acceptable way to do it? Or, can the specific properties in the Model inform the View that some of it's source has changed?
I have read about implementing the same OnPropertyChanged features in the Model classes. Thus the XAML will pick it up. But I thought those two parts of the MWWV we not supposed ot know about each other.
The thing is, the other 3 are in disabled controls, but they can be updated from two places on the window. So I don't think I can have two update source triggers can I?
Thank you.
Second attempt at explainign things:
ObservableCollection of Meeting objects. Bound to a ComboBox:
<ComboBox x:Name="comboMeetingWeek" ItemsSource="{Binding Meetings}"
SelectedItem="{Binding Meeting, UpdateSourceTrigger=PropertyChanged}" />
The Meeting object contains several properties. We bind controls on the window with these properties. Example:
<ComboBox x:Name="comboNotes" IsEditable="True"
DataContext="{Binding Meeting}"
Text="{Binding Note, UpdateSourceTrigger=LostFocus}"
ItemsSource="{StaticResource Notes}"/>
I do this for the majority of the controls. So the Meeting property in the view model is kept up to date and then when you select a different meeting it commits it to the model data and displays the new meeting (as previously described).
But, in some places on the window, I have some disabled text boxes. These are associated with properties nested inside the Meeting object. For example:
<TextBox x:Name="textBibleReadingMain" Grid.Column="0" Margin="2" IsEnabled="False"
DataContext="{Binding TFGW.BibleReadingItem.Main}"
Text="{Binding DataContext.BibleReadingMain, ElementName=oclmEditor, Mode=TwoWay, NotifyOnSourceUpdated=True, UpdateSourceTrigger=PropertyChanged}"/>
The parent TabItem already has it's DataContext set to {Binding Meeting}. What we need to display in the text box is:
Meeting (current context).TFGW.BibleReadingItem.Main.Name
This is why I had to do it has I did. For the above text box, this is what I want to allow to happen:
It should display the content of Meeting.TFGW.BibleReadingItem.Main.Name (Meeting already being a bound property).
As you select a different meeting from the dates combo, this text box should update.
If the user selects a name from the DataGrid and the ActiveAstudentAssignmentType combo is set to StudentAssignmentType::BibleReadingMain then I also want to update the text box.
I think what I am getting confused about is when I am supposed to derive my classes from INotifyPropertyChanged. My Model data is the Meeting objects with it's own data. Should all of these be inheriting from INotifyPropertyChanged and raising OnPropertyChanged? At the moment I do not have that implemented anywhere. I tell a lie, the only place I implemented it was for the view model itself:
public class OCLMEditorViewModel : INotifyPropertyChanged
So that is why I had to do it the way I did.
Any clearer?
Based on all the comments and further reasearch ....
One of the answers stated:
Viewmodel is created and wraps model
Viewmodel subscribes to model's PropertyChanged event
Viewmodel is set as view's DataContext, properties are bound etc
View triggers action on viewmodel
Viewmodel calls method on model
Model updates itself
Viewmodel handles model's PropertyChanged and raises its own PropertyChanged in response
View reflects the changes in its bindings, closing the feedback loop
I also read a bit of this (which confused me somewhat) where it stated:
The Model notifies the ViewModel if the data in the underlying data store has changed.
So, the first thing I did was change my Meeting object to derive from INotifyPropertyChanged. In addition, I added new properties for gaining access to deeper data in the Meeting model. Example (stripped down):
public class Meeting : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
#region Bible Reading Name Properties
[XmlIgnore]
public string BibleReadingMainName
{
get { return _TFGW.BibleReadingItem.Main.Name; }
set
{
_TFGW.BibleReadingItem.Main.Name = value;
OnPropertyChanged("BibleReadingMainName");
}
}
[XmlIgnore]
public string BibleReadingClass1Name
{
get { return _TFGW.BibleReadingItem.Class1.Name; }
set
{
_TFGW.BibleReadingItem.Class1.Name = value;
OnPropertyChanged("BibleReadingClass1Name");
}
}
[XmlIgnore]
public string BibleReadingClass2Name
{
get { return _TFGW.BibleReadingItem.Class2.Name; }
set
{
_TFGW.BibleReadingItem.Class2.Name = value;
OnPropertyChanged("BibleReadingClass2Name");
}
}
#endregion
protected void OnPropertyChanged(string name)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(name));
}
}
}
In my ViewModel I set it as a listener for PropertyChanged:
_Meeting.PropertyChanged += Meeting_PropertyChanged;
At this point in time, the handler just relays the property that was changed:
private void Meeting_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
OnPropertyChanged(e.PropertyName);
}
In my XAML, I adjust my TextBox to work with the new property, and I remove the DataContext reference. So I now have:
<TextBox x:Name="textBibleReadingMain" Grid.Column="0" Margin="2" IsEnabled="False"
Text="{Binding BibleReadingMainName, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}"/>
ON the right, where I have the DataGrid, when we click a row and the SelectedStudentItem is updated, we can now do:
private Student _SelectedStudentItem;
public Student SelectedStudentItem
{
get
{
return _SelectedStudentItem;
}
set
{
// We need to remove this item from the previous student history
if (_SelectedStudentItem != null)
_SelectedStudentItem.History.Remove(Meeting.DateMeeting);
_SelectedStudentItem = value;
if (_SelectedStudentItem == null)
return;
_EditStudentButtonClickCommand.RaiseCanExecuteChanged();
_DeleteStudentButtonClickCommand.RaiseCanExecuteChanged();
OnPropertyChanged("SelectedStudentItem");
if (ActiveStudentAssignmentType == StudentAssignmentType.BibleReadingMain)
_Meeting.BibleReadingMainName = _SelectedStudentItem.Name;
else if (ActiveStudentAssignmentType == StudentAssignmentType.BibleReadingClass1)
_Meeting.BibleReadingClass1Name = _SelectedStudentItem.Name;
else if (ActiveStudentAssignmentType == StudentAssignmentType.BibleReadingClass2)
_Meeting.BibleReadingClass2Name = _SelectedStudentItem.Name;
}
Based on the current ActiveStudentAssignmentType value we can directly update the source property. Thus the TextBox will automatically know about it due to the PropertyChange listener.
Thus, the original Meeting property code now looks like this:
public Data.MeetingInfo.Meeting Meeting
{
get { return _Meeting; }
set
{
// Has the existing meeting object changed at all?
if(_Meeting != null && _Meeting.IsDirty)
{
// Yes, so save it
_Model.Serialize();
_Meeting.MarkClean();
}
// Now we can update to new value
if (value != null)
{
_Meeting = value;
OnPropertyChanged("Meeting");
}
}
}
private Data.MeetingInfo.Meeting _Meeting;
All of those extra OnPropertyChanged calls are now obsolete!
The thing I was missing was implementing Notification from the Model to the ViewModel. And then the ViewModel informing the View.

Correct approach to UserControl creation when using MVVM

This is more of a conceptual question rather than a practical one. I'm just starting to learn the MVVM concept for developing UI , and I've come across a dillema I'm not sure the answer to:
Say I have a main window and a little pop-up window (meaning it's a small window with some UI elements in it). The structure of the program will look something like this:
MainWindow
model <-- MainWindowViewModel.cs <-- MainWindowView.xaml (containing no code-behind)
PopUpWindow (A UserControl)
model <-- PopUpWindowViewModel.cs <-- PopUpWindowView.xaml (containing no code-behind)
*the model is just a bunch of BL classes that are irrelevant for this question.
Now , lets say I want to create a new PopUp window from inside the MainWindowViewModel (or even save an instance of it in a private data-member). What is the correct way of doing so?
The way I see it I can't do something like this :
PopUpWindow pop = new PopUpWindow()
Because it kind of defeats the purpose of abstracting the view from the view model(What if a year from now i'll want to create a better version of the PopUpWindow using the same PopUpWindowViewModel?).
On the other hand , I can't initialize a new instnace of the PopUpWindow using just it's view model (The viewModel as I understand is not supposed to know anything about the view that will use it).
Hope it all makes sense... so what would you do in that situation?
*Just to clarify it further , let's say for argument's sake that the situation I'm describing is a button on the MainWindowView that upon clicking will open a PopUpWindowView.
Thanks in advnace.
I had somewhat a similar dilemma and I'll explain how I solved it.
Let's say you have MainWindow and a SettingsWindow, which you want to display when the SettingsButton is clicked.
You have two respective view models, MainWindowViewModel and SettingsViewModel, which you will be passing as their Window.DataContext properties.
Your MainWindowViewModel should expose an ICommand property named SettingsButtonCommand (or similar). Bind this command to the SettingsButton.Command.
Now your command should invoke something like this:
void OnSettingsButtonClicked()
{
var viewModel = new SettingsViewModel();
var window = new SettingsWindow();
window.DataContext = viewModel;
window.Show();
}
There is a slight issue when you want to use Window.ShowDialog(), because you need to resume execution.
For these cases I have an asynchronous variant of the DelegateCommand:
public sealed class AsyncDelegateCommand : ICommand
{
readonly Func<object, Task> onExecute;
readonly Predicate<object> onCanExecute;
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
public AsyncDelegateCommand(Func<object, Task> onExecute)
: this(onExecute, null) { }
public AsyncDelegateCommand(Func<object, Task> onExecute, Predicate<object> onCanExecute)
{
if (onExecute == null)
throw new ArgumentNullException("onExecute");
this.onExecute = onExecute;
this.onCanExecute = onCanExecute;
}
#region ICommand Methods
public async void Execute(object parameter)
{
await onExecute(parameter);
}
public bool CanExecute(object parameter)
{
return onCanExecute != null ? onCanExecute(parameter) : true;
}
#endregion
}
You've specifically said that the popup is a UserControl so you can use basic data templating. First create view models for your main window and popup control:
public class MainViewModel : ViewModelBase
{
private PopUpViewModel _PopUp;
public PopUpViewModel PopUp
{
get { return _PopUp; }
set { _PopUp = value; RaisePropertyChanged(() => this.PopUp); }
}
}
public class PopUpViewModel : ViewModelBase
{
private string _Message;
public string Message
{
get { return _Message; }
set { _Message = value; RaisePropertyChanged(() => this.Message); }
}
}
The MainViewModel's PopUp member is initially null, we'll set it to an instance of PopUpViewModel when we want the popup to appear. To do that we create a content control on the main window and set it's content to that member. We also use a data template to specify the type of child control to create when the popup view model has been set:
<Window.Resources>
<DataTemplate DataType="{x:Type local:PopUpViewModel}">
<local:PopUpWindow />
</DataTemplate>
</Window.Resources>
<StackPanel>
<Button Content="Show PopUp" Click="Button_Click_1" HorizontalAlignment="Left"/>
<ContentControl Content="{Binding PopUp}" />
</StackPanel>
I'm doing a big no-no here by creating the view model in the code-behind along with a click handler, but it's just for illustrative purposes:
public partial class MainWindow : Window
{
MainViewModel VM = new MainViewModel();
public MainWindow()
{
InitializeComponent();
this.DataContext = this.VM;
}
private void Button_Click_1(object sender, RoutedEventArgs e)
{
this.VM.PopUp = new PopUpViewModel { Message = "Hello World!" };
}
}
That's it! Click the button, popup window appears underneath it showing the content. Now it may not always be this simple, sometimes you may want to create multiple children on a parent control...in that case you'd set up an ItemsControl, set its panel to a Grid (say) and modify the data templates to set the Margin etc on each element to position them. Or you may not always know what type of view model is going to be created, in which case you need to add multiple data templates for each type you're expecting. Either way you still have good separation of concerns because it is the views that are deciding how to display the content in the view models. The view models themselves still don't know anything about the views and they can be unit-tested etc independently.

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);

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