MVVM exploration: Best practices for disabling windows from ViewModel, mvvmviewmodel
When using MVVM for development in WPF, it seems that there is always an inevitable problem: ViewModel needs to close this Window after processing the business. What should we do at this time?
There are many solutions on the Internet: Some throw an event in ViewModel and use (XXXViewModel) this on the View side. dataContext responds to events, while Trigger, Behavior, Action, and other third-party frameworks are used.
These operations can indeed implement this function at a certain level, but some operations are too troublesome, some implement functions, but they greatly violate the MVVM principle, some have many limitations (for example, you can only do nothing after the Window is closed, or you must require constructors with or without Window parameters ). After I found that such an operation can still be performed, I think this should be the best practice to solve this problem: elegance, conciseness, and there is no limitation in keeping with the MVVM idea.
In MVVM, most of the operations are performed between View and ViewModel through binding. This is also the most Recommended Practice for MVVM. So why can't we bind a View to close it? Is it because Window does not control the attribute for closing this operation? No. When MVVM is not used and code is directly written in the background to create a Window, we only need to assign values to the DialogResult attribute of the Window (whether true or false) you can close this window. So why don't we directly bind the DialogResult attribute of the Window to the ViewModel?
With this idea, I did this experiment, compiled it, and got the following exception prompt during running:
"Binding" cannot be set on the "DialogResult" attribute of the "ChildWindow" type ". You can only set "Binding" on DependencyProperty of DependencyObject ".
This prompt is already obvious: Why can't we directly bind DialogResult of Window, because DialogResult is not a dependency attribute, and all bindings in WPF must only bind dependency attributes, most of the properties in WPF are dependency attributes, but DialogResult is not dependency attributes, so it cannot be bound.
The above solutions are available after the failure, but why don't you think that DialogResult is not a dependency attribute? Isn't it enough to register a dependency attribute? WPF does not prevent registration.
The code for registering dependency properties is as follows:
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); } }
Then bind the dependency attribute DialogResult on The View side:
<Window x:Class="mvvm_demo_close_window.ChildWindow" ... xmlns:xc="clr-namespace:mvvm_demo_close_window" xc:DialogCloser.DialogResult="{Binding DialogResult}">
Then, you can operate on the ViewModel side as a normal Dependency Property. When this. DialogResult = true, the Child Window is automatically closed in the ViewModel:
Public class ChildWindowViewModel: ViewModelBase {private bool? DialogResult; public bool? DialogResult {get {return this. dialogResult;} set {this. dialogResult = value; RaisePropertyChanged ("DialogResult") ;}}// Command public ICommand CloseCmd {get {return new DelegateCommand (obj) =>{ this. dialogResult = true ;});}}}
This is the most elegant solution I have found. DialogCloser can also be reused completely. If you have a better solution, you are welcome to discuss it together. The source code is provided below, and you need to download it yourself.
Click to download source code