How to bind Close command to a button
Asked Answered
V

7

67

The easiest way is to implement ButtonClick event handler and invoke Window.Close() method, but how doing this through a Command binding?

Vermont answered 30/6, 2009 at 20:32 Comment(0)
R
57

I think that in real world scenarios a simple click handler is probably better than over-complicated command-based systems but you can do something like that:

using RelayCommand from this article http://msdn.microsoft.com/en-us/magazine/dd419663.aspx

public class MyCommands
{
    public static readonly ICommand CloseCommand =
        new RelayCommand( o => ((Window)o).Close() );
}
<Button Content="Close Window"
        Command="{X:Static local:MyCommands.CloseCommand}"
        CommandParameter="{Binding RelativeSource={RelativeSource FindAncestor, 
                           AncestorType={x:Type Window}}}"/>
Roby answered 1/7, 2009 at 14:18 Comment(1)
Or if you don't want to use the RelayCommand, write a custom Command: class WindowCloseCmd : ICommand { public bool CanExecute(object parameter) { return true; } public event EventHandler CanExecuteChanged { add { } remove { } } public void Execute(object wnd) { if (wnd is Window) ((Window)wnd).Close(); } } public static readonly ICommand WindowCloseCommand = new WindowCloseCmd();Fuze
G
83

All it takes is a bit of XAML...

<Window x:Class="WCSamples.Window1"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    <Window.CommandBindings>
        <CommandBinding Command="ApplicationCommands.Close"
                        Executed="CloseCommandHandler"/>
    </Window.CommandBindings>
    <StackPanel Name="MainStackPanel">
        <Button Command="ApplicationCommands.Close" 
                Content="Close Window" />
    </StackPanel>
</Window>

And a bit of C#...

private void CloseCommandHandler(object sender, ExecutedRoutedEventArgs e)
{
    this.Close();
}

(adapted from this MSDN article)

Gorge answered 30/6, 2009 at 21:2 Comment(3)
phew... I thought it's possible to do this without c# code. Then It seems better to do this in an old way of defining ButtonClick behavior.Vermont
The benefit of using commands is that they aren't tied to specific UI controls; you can use this one command to close your application using a button, a menu option, or even a keyboard shortcut, all through the same command. And, while the functionality doesn't really apply to the 'Close' operation, commands also provide a way to automatically disable the corresponding UI control when they aren't able to be fired - the way 'Paste' is disabled when no text is in the clipboard.Gorge
+1 For using the built in ApplicationCommands.Close command.Brittani
B
71

Actually, it is possible without C# code. The key is to use interactions:

<Button Content="Close">
  <i:Interaction.Triggers>
    <i:EventTrigger EventName="Click">
      <ei:CallMethodAction TargetObject="{Binding ElementName=window}" MethodName="Close"/>
    </i:EventTrigger>
  </i:Interaction.Triggers>
</Button>

In order for this to work, just set the x:Name of your window to "window", and add these two namespaces:

xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity" 
xmlns:ei="http://schemas.microsoft.com/expression/2010/interactions"

This requires that you add the Expression Blend SDK DLL to your project, specifically Microsoft.Expression.Interactions.

In case you don't have Blend, the SDK can be downloaded here.

Barbabra answered 12/4, 2011 at 11:51 Comment(4)
BTW, is it possible to pass any params using this?Metopic
it wouldn't recognize the ei: section until I added Microsoft.Expression.Interactions as a referenceBusch
I can't believe how long it took me to find this answer. way too much overkill in most solutions, as long as you don't mind using the blend sdk files.Mamoun
instead of ElementName you could use this TargetObject="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type Window}}}"Uncut
R
57

I think that in real world scenarios a simple click handler is probably better than over-complicated command-based systems but you can do something like that:

using RelayCommand from this article http://msdn.microsoft.com/en-us/magazine/dd419663.aspx

public class MyCommands
{
    public static readonly ICommand CloseCommand =
        new RelayCommand( o => ((Window)o).Close() );
}
<Button Content="Close Window"
        Command="{X:Static local:MyCommands.CloseCommand}"
        CommandParameter="{Binding RelativeSource={RelativeSource FindAncestor, 
                           AncestorType={x:Type Window}}}"/>
Roby answered 1/7, 2009 at 14:18 Comment(1)
Or if you don't want to use the RelayCommand, write a custom Command: class WindowCloseCmd : ICommand { public bool CanExecute(object parameter) { return true; } public event EventHandler CanExecuteChanged { add { } remove { } } public void Execute(object wnd) { if (wnd is Window) ((Window)wnd).Close(); } } public static readonly ICommand WindowCloseCommand = new WindowCloseCmd();Fuze
S
54

If the window was shown with Window.ShowDialog():

The simplest solution that I know of is to set the IsCancel property to true of the close Button:

<Button Content="Close" IsCancel="True" />

No bindings needed, WPF will do that for you automatically!

This properties provide an easy way of saying these are the "OK" and "Cancel" buttons on a dialog. It also binds the ESC key to the button.

Reference: MSDN Button.IsCancel property.

Sulphuric answered 31/10, 2014 at 16:25 Comment(1)
IsCancel="True" will be a better choice if you are having a Modal window / Dialog Window because only thing it does is to call cancel on the window.Since normal window doesnt respond to Cancelling it doesn't work.Behn
B
5

For .NET 4.5 SystemCommands class will do the trick (.NET 4.0 users can use WPF Shell Extension google - Microsoft.Windows.Shell or Nicholas Solution).

    <Window.CommandBindings>
        <CommandBinding Command="{x:Static SystemCommands.CloseWindowCommand}" 
                        CanExecute="CloseWindow_CanExec" 
                        Executed="CloseWindow_Exec" />
    </Window.CommandBindings>
    <!-- Binding Close Command to the button control -->
    <Button ToolTip="Close Window" Content="Close" Command="{x:Static SystemCommands.CloseWindowCommand}"/>

In the Code Behind you can implement the handlers like this:

    private void CloseWindow_CanExec(object sender, CanExecuteRoutedEventArgs e)
    {
        e.CanExecute = true;
    }

    private void CloseWindow_Exec(object sender, ExecutedRoutedEventArgs e)
    {
        SystemCommands.CloseWindow(this);
    }
Behn answered 15/4, 2015 at 16:53 Comment(1)
Why SystemCommands.CloseWindow(this); instead of this.Close() (or somewindow.Close()) ?Limpopo
D
3

In the beginning I was also having a bit of trouble figuring out how this works so I wanted to post a better explanation of what is actually going on.

According to my research the best way to handle things like this is using the Command Bindings. What happens is a "Message" is broadcast to everything in the program. So what you have to do is use the CommandBinding. What this essentially does is say "When you hear this Message do this".

So in the Question the User is trying to Close the Window. The first thing we need to do is setup our Functions that will be called when the SystemCommand.CloseWindowCommand is broadcast. Optionally you can assign a Function that determines if the Command should be executed. An example would be closing a Form and checking if the User has saved.

MainWindow.xaml.cs (Or other Code-Behind)

void CloseApp( object target, ExecutedRoutedEventArgs e ) {
    /*** Code to check for State before Closing ***/
    this.Close();
}

void CloseAppCanExecute( object sender, CanExecuteRoutedEventArgs e ) {
    /*** Logic to Determine if it is safe to Close the Window ***/
    e.CanExecute = true;
}

Now we need to setup the "Connection" between the SystemCommands.CloseWindowCommand and the CloseApp and CloseAppCanExecute

MainWindow.xaml (Or anything that implements CommandBindings)

<Window.CommandBindings>
    <CommandBinding Command="SystemCommands.CloseWindowCommand"
                    Executed="CloseApp"
                    CanExecute="CloseAppCanExecute"/>
</Window.CommandBindings>

You can omit the CanExecute if you know that the Command should be able to always be executed Save might be a good example depending on the Application. Here is a Example:

<Window.CommandBindings>
    <CommandBinding Command="SystemCommands.CloseWindowCommand"
                    Executed="CloseApp"/>
</Window.CommandBindings>

Finally you need to tell the UIElement to send out the CloseWindowCommand.

<Button Command="SystemCommands.CloseWindowCommand">

Its actually a very simple thing to do, just setup the link between the Command and the actual Function to Execute then tell the Control to send out the Command to the rest of your program saying "Ok everyone run your Functions for the Command CloseWindowCommand".

This is actually a very nice way of handing this because, you can reuse the Executed Function all over without having a wrapper like you would with say WinForms (using a ClickEvent and calling a function within the Event Function) like:

protected override void OnClick(EventArgs e){
    /*** Function to Execute ***/
}

In WPF you attach the Function to a Command and tell the UIElement to execute the Function attached to the Command instead.

I hope this clears things up...

Declamation answered 23/2, 2017 at 14:12 Comment(0)
P
0

One option that I've found to work is to set this function up as a Behavior.

The Behavior:

    public class WindowCloseBehavior : Behavior<Window>
{
    public bool Close
    {
        get { return (bool) GetValue(CloseTriggerProperty); }
        set { SetValue(CloseTriggerProperty, value); }
    }

    public static readonly DependencyProperty CloseTriggerProperty =
        DependencyProperty.Register("Close", typeof(bool), typeof(WindowCloseBehavior),
            new PropertyMetadata(false, OnCloseTriggerChanged));

    private static void OnCloseTriggerChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        var behavior = d as WindowCloseBehavior;

        if (behavior != null)
        {
            behavior.OnCloseTriggerChanged();
        }
    }

    private void OnCloseTriggerChanged()
    {
        // when closetrigger is true, close the window
        if (this.Close)
        {
            this.AssociatedObject.Close();
        }
    }
}

On the XAML Window, you set up a reference to it and bind the Behavior's Close property to a Boolean "Close" property on your ViewModel:

xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
<i:Interaction.Behaviors>
    <behavior:WindowCloseBehavior Close="{Binding Close}" />
</i:Interaction.Behaviors>

So, from the View assign an ICommand to change the Close property on the ViewModel which is bound to the Behavior's Close property. When the PropertyChanged event is fired the Behavior fires the OnCloseTriggerChanged event and closes the AssociatedObject... which is the Window.

Phrygian answered 18/7, 2018 at 18:25 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.