WPF MVVM: How to close a window
I have a Button
that closes my window when it's clicked:
<Button x:Name="buttonOk" IsCancel="True">Ok</Button>
That's fine until I add a Command
to the Button
ie
<Button x:Name="buttonOk"
Command="{Binding SaveCommand}"
IsCancel="True">Ok</Button>
Now it doesn't close presumably because I am handling the Command
. I can fix this by putting an EventHandler
in and calling this.Close()
ie
<Button x:Name="buttonOk"
Click="closeWindow"
Command="{Binding SaveCommand}"
IsCancel="True">Ok</Button>
but now I have code in my code behind ie the method SaveCommand
. I am using the MVVM pattern and SaveCommand
is the only code in my code behind.
How can I do this differently so as not to use code behind?
I just completed a blog post on this very topic. In a nutshell, add an Action
property to your ViewModel with get
and set
accessors. Then define the Action
from your View
constructor. Finally, invoke your action in the bound command that should close the window.
In the ViewModel:
public Action CloseAction { get; set;}
and in the View
constructor:
private View()
{
InitializeComponent();
ViewModel vm = new ViewModel();
this.DataContext = vm;
if ( vm.CloseAction == null )
vm.CloseAction = new Action(this.Close);
}
Finally, in whatever bound command that should close the window, we can simply invoke
CloseAction(); // Calls Close() method of the View
This worked for me, seemed like a fairly elegant solution, and saved me a bunch of coding.
Unfortunately displaying windows is a real pain in MVVM so you need to do quite a bit of infrastructure work or use a MVVM framework like Cinch. If you want to invest the time to do it yourself here's a link of how Cinch does it.
Its good that you're trying to keep any logic out of the View but its really not the end of the world if you do. In this instance it doesn't sound like it would cause too many problems.
As someone commented, the code I have posted is not MVVM friendly, how about the second solution?
1st, not MVVM solution (I will not delete this as a reference)
XAML:
<Button Name="okButton" Command="{Binding OkCommand}" CommandParameter="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}}">OK</Button>
ViewModel:
public ICommand OkCommand
{
get
{
if (_okCommand == null)
{
_okCommand = new ActionCommand<Window>(DoOk, CanDoOk);
}
return _okCommand ;
}
}
void DoOk(Window win)
{
// Your Code
win.DialogResult = true;
win.Close();
}
bool CanDoOk(Window win) { return true; }
2nd, probably better solution: Using attached behaviours
XAML
<Button Content="Ok and Close" Command="{Binding OkCommand}" b:CloseOnClickBehaviour.IsEnabled="True" />
View Model
public ICommand OkCommand
{
get { return _okCommand; }
}
Behaviour Class Something similar to this:
public static class CloseOnClickBehaviour
{
public static readonly DependencyProperty IsEnabledProperty =
DependencyProperty.RegisterAttached(
"IsEnabled",
typeof(bool),
typeof(CloseOnClickBehaviour),
new PropertyMetadata(false, OnIsEnabledPropertyChanged)
);
public static bool GetIsEnabled(DependencyObject obj)
{
var val = obj.GetValue(IsEnabledProperty);
return (bool)val;
}
public static void SetIsEnabled(DependencyObject obj, bool value)
{
obj.SetValue(IsEnabledProperty, value);
}
static void OnIsEnabledPropertyChanged(DependencyObject dpo, DependencyPropertyChangedEventArgs args)
{
var button = dpo as Button;
if (button == null)
return;
var oldValue = (bool)args.OldValue;
var newValue = (bool)args.NewValue;
if (!oldValue && newValue)
{
button.Click += OnClick;
}
else if (oldValue && !newValue)
{
button.PreviewMouseLeftButtonDown -= OnClick;
}
}
static void OnClick(object sender, RoutedEventArgs e)
{
var button = sender as Button;
if (button == null)
return;
var win = Window.GetWindow(button);
if (win == null)
return;
win.Close();
}
}
链接地址: http://www.djcxy.com/p/56114.html
上一篇: 在不使用System.Windows.Input.ICommand的情况下在WPF中实现MVVM
下一篇: WPF MVVM:如何关闭一个窗口