Reputation: 45262
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
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
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
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
Reputation: 11
private void MenuItem_Click(object sender, RoutedEventArgs e)
{
Application.Current.Shutdown();
}
Upvotes: 1
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
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
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