Andrew Shepherd
Andrew Shepherd

Reputation: 45262

WPF Standard Commands - Where's Exit?

I'm creating a standard menu in my WPF application.

I know that I can create custom commands, but I know there are also a bunch of standard commands to bind to.

For example, to open a file I should bind to ApplicationCommands.Open, to close a file I should bind to ApplicationCommands.Close. There's also a large number of EditCommands, ComponentCommands or NavigationCommands.

There doesn't seem to be an "Exit" command. I would have expected there to be ApplicationCommands.Exit.

What should I bind to the "Exit" menu item? To create a custom command for something this generic just seems wrong.

Upvotes: 26

Views: 23871

Answers (7)

Peter Duniho
Peter Duniho

Reputation: 70701

Yes, it would've made a lot of sense for Microsoft to include an ApplicationCommands.Exit command in their collection of pre-defined commands. It disappoints me that they didn't. But as the answers to this question demonstrate, not all is lost.

There are lots of workarounds possible for the lack of a proper ApplicationCommands.Exit object. However, I feel most miss the point. Either they implement something in the view model, for something that really is strictly a view behavior (in some cases reaching into the view object graph with e.g. Application.Current.MainWindow!), or they write a bunch of code-behind to do what XAML does very well and much more conveniently.

IMHO, the simplest approach is just to declare a RoutedUICommand resource for the window, attach that to a command binding, and to a menu item, to hook all the parts up. For example:

<Window x:Class="ConwaysGameOfLife.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">

  <Window.Resources>
    <RoutedUICommand x:Key="fileExitCommand" Text="File E_xit">
      <RoutedUICommand.InputGestures>
        <KeyGesture >Alt+F4</KeyGesture>
      </RoutedUICommand.InputGestures>
    </RoutedUICommand>
  </Window.Resources>

  <Window.CommandBindings>
    <CommandBinding Command="{StaticResource fileExitCommand}" Executed="fileExitCommand_Executed"/>
  </Window.CommandBindings>

  <DockPanel>
    <Menu DockPanel.Dock="Top">
      <MenuItem Header="_File">
        <!-- other menu items go here -->
        <Separator/>
        <MenuItem Command="{StaticResource fileExitCommand}"/>
      </MenuItem>
    </Menu>
    <!-- the main client area UI goes here -->
  </DockPanel>

</Window>

The Executed event handler for the command binding is trivial:

private void fileExitCommand_Executed(object sender, ExecutedRoutedEventArgs e)
{
    Close();
}

Assuming, of course, your program implementation follows the usual semantics of closing the main window to exit the program.

In this way, all of the UI-specific elements go right into the RoutedUICommand object, which can be configured correctly and conveniently right in the XAML rather than having to declare a new C# class to implement the command and/or mess around with the input bindings from code-behind. The MenuItem object already knows what to do with a RoutedUICommand in terms of display, so going this route provides a nice decoupling of the command's properties from the rest of the UI. It also provides a convenient way to provide a secondary key gesture, in case you'd prefer something other than the default Alt+F4 (e.g. Ctrl+W).

You can even put the RoutedUICommand declaration in the App.xaml file, for reuse among multiple windows in your program, should they exist. Again, decoupling the UI-specific aspects, which are declared in the resource, from the consumers that are found throughout the program.

I find this approach much more generalizable and easily implemented than the other options which I've seen presented (here and elsewhere).

Upvotes: 4

Maxence
Maxence

Reputation: 13329

Pretty simple to do:

using System.Windows.Input;

namespace YourApp
{
    static class ApplicationCommands
    {    
        public static RoutedCommand Quit { get; }    

        static ApplicationCommands()
        {
            var inputGestures = new InputGestureCollection();
            inputGestures.Add(new KeyGesture(Key.Q, ModifierKeys.Control));
            Quit = new RoutedUICommand("Quit", "Quit", typeof(ApplicationCommands),
                inputGestures);
        }
    }
}

Use it in your XAML like this:

<Window.CommandBindings>
    <CommandBinding Command="local:ApplicationCommands.Quit" 
                    Executed="QuitCommandOnExecuted"/>
</Window.CommandBindings>

[..]

<MenuItem Command="local:ApplicationCommands.Quit" />

Code-behind:

void QuitCommandOnExecuted(object sender, ExecutedRoutedEventArgs e)
{
    Application.Current.Shutdown();
}

Upvotes: 1

dthrasher
dthrasher

Reputation: 41822

Unfortunately, there is no predefined ApplicationCommands.Exit. Adding one to WPF was suggested on Microsoft Connect in 2008: http://connect.microsoft.com/VisualStudio/feedback/details/354300/add-predefined-wpf-command-applicationcommands-exit. The item has been marked closed/postponed, however.

Mike Taulty discussed how to create your own Exit command in an article on his blog.

Upvotes: 11

Alex
Alex

Reputation: 11

private void MenuItem_Click(object sender, RoutedEventArgs e)
    {
        Application.Current.Shutdown();
    }

Upvotes: 1

NoOne
NoOne

Reputation: 4091

Not that complex actually (but still, M$ sucks for not providing it). Here you go:

public static class MyCommands
{
    private static readonly ICommand appCloseCmd = new ApplicationCloseCommand();
    public static ICommand ApplicationCloseCommand
    {
        get { return appCloseCmd; }
    }
}

//===================================================================================================
public class ApplicationCloseCommand : ICommand
{
    public event EventHandler CanExecuteChanged
    {
        // You may not need a body here at all...
        add { CommandManager.RequerySuggested += value; }
        remove { CommandManager.RequerySuggested -= value; }
    }

    public bool CanExecute(object parameter)
    {
        return Application.Current != null && Application.Current.MainWindow != null;
    }

    public void Execute(object parameter)
    {
        Application.Current.MainWindow.Close();
    }
}

And the body of the AplicationCloseCommand.CanExecuteChanged event handler may not be even needed.

You use it like so:

<MenuItem Header="{DynamicResource MenuFileExit}" Command="MyNamespace:MyCommands.ApplicationCloseCommand"/>

Cheers!

(You cannot imagine how long it took me to discover this Command stuff myself...)

Upvotes: 6

Thomas Levesque
Thomas Levesque

Reputation: 292625

AFAIK there's no ApplicationCommands.Quit or ApplicationCommands.Exit, so I guess you're gonna have to create it yourself...

Anyway, if you're using the MVVM pattern, RoutedCommands are not exactly handy, so it's better to use a lightweight alternative like RelayCommand or DelegateCommand.

Upvotes: 7

karl.r
karl.r

Reputation: 971

You can modify the Text property of a command before you bind it in a window. This will change the text of the command everywhere it appears in your ui.

EDIT: Do this to Close in the Window or App ctor

Upvotes: -1

Related Questions