Blog Archives
WPF Commands (part 2)
Let’s start with a program that uses Cut, Copy and Paste in two TextBoxes without writing any C# code. This is not a typo. We only need XAML for this.
<Window x:Class="DemoApp.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="MainWindow" Height="400" Width="400"> <DockPanel LastChildFill="True" > <Menu DockPanel.Dock="Top"> <MenuItem Header="_Edit"> <MenuItem Command="{x:Static ApplicationCommands.Cut}" CommandParameter="Cut it!"/> <MenuItem Command="{x:Static ApplicationCommands.Copy}" CommandParameter="Copy it!"/> <MenuItem Command="{x:Static ApplicationCommands.Paste}" CommandParameter="Paste it!"/> </MenuItem> </Menu> <ToolBarTray Background="Gray" DockPanel.Dock="Top"> <ToolBar Band="0" BandIndex="0" > <Button Command="{x:Static ApplicationCommands.Cut}" Content="Cut" /> <Button Command="{x:Static ApplicationCommands.Copy}" Content="Copy" /> <Button Command="{x:Static ApplicationCommands.Paste}" Content="Paste" /> </ToolBar> <ToolBar Band="1" BandIndex="1"> <ToolBarPanel Orientation="Vertical"> <Label Content="Dummy0" ToolBar.OverflowMode="AsNeeded" /> <Label Content="Dummy1" ToolBar.OverflowMode="AsNeeded" /> <Label Content="Dummy2" ToolBar.OverflowMode="AsNeeded" /> </ToolBarPanel> </ToolBar> </ToolBarTray> <Grid> <Grid.ColumnDefinitions> <ColumnDefinition /> <ColumnDefinition Width="Auto"/> <ColumnDefinition /> </Grid.ColumnDefinitions> <TextBox TextWrapping="Wrap" Width="Auto" Grid.Column="0" Grid.ColumnSpan="1" Margin="0" >It happened that a Fox caught its tail in a trap, and in struggling to release himself lost all of it but the stump. At first he was ashamed to show himself among his fellow foxes.</TextBox> <GridSplitter Grid.Column="1" VerticalAlignment="Stretch" HorizontalAlignment="Stretch" Margin="1,0,0,0" Width="3"/> <TextBox TextWrapping="Wrap" Width="Auto" Grid.Column="2" Grid.ColumnSpan="1" Margin="0" >But at last he determined to put a bolder face upon his misfortune, and summoned all the foxes to a general meeting to consider a proposal which he had to place before them.</TextBox> </Grid> </DockPanel> </Window>
I added a Toolbar with some dummy labels just to keep the learning curve going. You remove them without any risk.
What is happening here?
Some input controls handle command events on their own. Everything is built-in already. All you need to do is to provide the Buttons or MenuItems which call these commands. The elements even enable/disable themselves. We have two textboxes in the example. These commands are applied to the element that has the focus.
How can this be achieved? The element finds the window instance and then determines what element was focused previously. This only works for Toolbars and Menus UNLESS you set the CommandTarget property manually.
Let’s add standard buttons now. You cannot see any effect when you press them. The buttons are even ghosted. To solve this we assign the names TextBox1 and TextBox2 and link the Button CommandTargets to these elements.
You can now cut or copy from TextBox1 and paste it into TextBox2.
<Window x:Class="DemoApp.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="MainWindow" Height="400" Width="400"> <DockPanel LastChildFill="True" > <Menu DockPanel.Dock="Top"> <MenuItem Header="_Edit"> <MenuItem Command="{x:Static ApplicationCommands.Cut}" CommandParameter="Cut"/> <MenuItem Command="{x:Static ApplicationCommands.Copy}" CommandParameter="Copy"/> <MenuItem Command="{x:Static ApplicationCommands.Paste}" CommandParameter="Paste"/> </MenuItem> </Menu> <ToolBarTray Background="Gray" DockPanel.Dock="Top"> <ToolBar Band="0" BandIndex="0" > <Button Command="{x:Static ApplicationCommands.Cut}" Content="Cut" /> <Button Command="{x:Static ApplicationCommands.Copy}" Content="Copy" /> <Button Command="{x:Static ApplicationCommands.Paste}" Content="Paste" /> </ToolBar> <ToolBar Band="1" BandIndex="1"> <ToolBarPanel Orientation="Vertical"> <Label Content="Dummy0" ToolBar.OverflowMode="AsNeeded" /> <Label Content="Dummy1" ToolBar.OverflowMode="AsNeeded" /> <Label Content="Dummy2" ToolBar.OverflowMode="AsNeeded" /> </ToolBarPanel> </ToolBar> </ToolBarTray> <!-- changed --> <StackPanel Orientation="Horizontal" DockPanel.Dock="Top"> <Button Command="{x:Static ApplicationCommands.Cut}" CommandTarget="{Binding ElementName=TextBox1}" Content="Cut" /> <Button Command="{x:Static ApplicationCommands.Copy}" CommandTarget="{Binding ElementName=TextBox1}" Content="Copy" /> <Button Command="{x:Static ApplicationCommands.Paste}" CommandTarget="{Binding ElementName=TextBox2}" Content="Paste" /> </StackPanel> <Grid> <Grid.ColumnDefinitions> <ColumnDefinition /> <ColumnDefinition Width="Auto"/> <ColumnDefinition /> </Grid.ColumnDefinitions> <!-- changed --> <TextBox Name="TextBox1" TextWrapping="Wrap" Width="Auto" Grid.Column="0" Grid.ColumnSpan="1" Margin="0" >It happened that a Fox caught its tail in a trap, and in struggling to release himself lost all of it but the stump. At first he was ashamed to show himself among his fellow foxes.</TextBox> <GridSplitter Grid.Column="1" VerticalAlignment="Stretch" HorizontalAlignment="Stretch" Margin="1,0,0,0" Width="3"/> <!-- changed --> <TextBox Name="TextBox2" TextWrapping="Wrap" Width="Auto" Grid.Column="2" Grid.ColumnSpan="1" Margin="0" >But at last he determined to put a bolder face upon his misfortune, and summoned all the foxes to a general meeting to consider a proposal which he had to place before them.</TextBox> </Grid> </DockPanel> </Window>
But hardcoding is a really bad approach. Therefore we are going to use FocusManager.IsFocusScope=”True” instead. WPF then checks the parent focus. By default, the Window class is a focus scope as are the Menu, ContextMenu, and ToolBar classes.
The following example is flawless.
<Window x:Class="DemoApp.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="MainWindow" Height="400" Width="400"> <DockPanel LastChildFill="True" > <Menu DockPanel.Dock="Top"> <MenuItem Header="_Edit"> <MenuItem Command="{x:Static ApplicationCommands.Cut}" CommandParameter="Cut"/> <MenuItem Command="{x:Static ApplicationCommands.Copy}" CommandParameter="Copy"/> <MenuItem Command="{x:Static ApplicationCommands.Paste}" CommandParameter="Paste"/> </MenuItem> </Menu> <ToolBarTray Background="Gray" DockPanel.Dock="Top"> <ToolBar Band="0" BandIndex="0" > <Button Command="{x:Static ApplicationCommands.Cut}" Content="Cut" /> <Button Command="{x:Static ApplicationCommands.Copy}" Content="Copy" /> <Button Command="{x:Static ApplicationCommands.Paste}" Content="Paste" /> </ToolBar> <ToolBar Band="1" BandIndex="1"> <ToolBarPanel Orientation="Vertical"> <Label Content="Dummy0" ToolBar.OverflowMode="AsNeeded" /> <Label Content="Dummy1" ToolBar.OverflowMode="AsNeeded" /> <Label Content="Dummy2" ToolBar.OverflowMode="AsNeeded" /> </ToolBarPanel> </ToolBar> </ToolBarTray> <!-- changed --> <StackPanel Orientation="Horizontal" DockPanel.Dock="Top" FocusManager.IsFocusScope="True"> <Button Command="{x:Static ApplicationCommands.Cut}" Content="Cut" /> <Button Command="{x:Static ApplicationCommands.Copy}" Content="Copy" /> <Button Command="{x:Static ApplicationCommands.Paste}" Content="Paste" /> </StackPanel> <Grid> <Grid.ColumnDefinitions> <ColumnDefinition /> <ColumnDefinition Width="Auto"/> <ColumnDefinition /> </Grid.ColumnDefinitions> <TextBox TextWrapping="Wrap" Width="Auto" Grid.Column="0" Grid.ColumnSpan="1" Margin="0" >It happened that a Fox caught its tail in a trap, and in struggling to release himself lost all of it but the stump. At first he was ashamed to show himself among his fellow foxes.</TextBox> <GridSplitter Grid.Column="1" VerticalAlignment="Stretch" HorizontalAlignment="Stretch" Margin="1,0,0,0" Width="3"/> <TextBox TextWrapping="Wrap" Width="Auto" Grid.Column="2" Grid.ColumnSpan="1" Margin="0" >But at last he determined to put a bolder face upon his misfortune, and summoned all the foxes to a general meeting to consider a proposal which he had to place before them.</TextBox> </Grid> </DockPanel> </Window>
The IsFocusScope approach has the advantage that the same commands apply to several controls.
Custom Commands
We are going to write our own commands now. For this we need to create a class and add a property that returns a RoutedUICommand instance. This property needs to be static. And to initialize this class you also need a static constructor.
using System.Windows.Input; namespace CustomCommands { public class PlaySound { static PlaySound() { KeyGesture lShortCut = new KeyGesture(Key.P, ModifierKeys.Control, "Ctrl+p"); InputGestureCollection InputGestureCollection = new InputGestureCollection(); InputGestureCollection.Add(lShortCut); PlaySoundCommand = new RoutedUICommand("Play", "PlaySound", typeof(PlaySound), InputGestureCollection); } // static constructor public static RoutedUICommand PlaySoundCommand { get; private set; } } // class } // namespace
The MainWindow class should look like this. The method CommandBinding_PlaySound_Executed plays the system beep sound.
using System.Windows; using System.Windows.Input; namespace DemoApp { public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); } private void CommandBinding_PlaySound_Executed(object sender, ExecutedRoutedEventArgs e) { System.Media.SystemSounds.Beep.Play(); MessageBox.Show("Source: " + e.Source.ToString() + Environment.NewLine + "OriginalSource: " + e.OriginalSource.ToString() + Environment.NewLine + "Parameter: " + e.Parameter.ToString()); } // } // class } // namespace
Add the class in your XAML namespace. I used xmlns:c=”clr-namespace:CustomCommands” .
<Window x:Class="DemoApp.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:c="clr-namespace:CustomCommands" Title="MainWindow" Height="400" Width="400"> <!-- above was changed --> <Window.CommandBindings> <!-- changed --> <CommandBinding Command="c:PlaySound.PlaySoundCommand" Executed="CommandBinding_PlaySound_Executed" /> </Window.CommandBindings> <DockPanel LastChildFill="True" > <Menu DockPanel.Dock="Top"> <MenuItem Header="_Edit"> <MenuItem Command="{x:Static ApplicationCommands.Cut}" CommandParameter="Cut"/> <MenuItem Command="{x:Static ApplicationCommands.Copy}" CommandParameter="Copy"/> <MenuItem Command="{x:Static ApplicationCommands.Paste}" CommandParameter="Paste"/> </MenuItem> <MenuItem Header="_Media"> <MenuItem Command="c:PlaySound.PlaySoundCommand" CommandParameter="Play"/> </MenuItem> </Menu> <ToolBarTray Background="Gray" DockPanel.Dock="Top"> <ToolBar Band="0" BandIndex="0" > <Button Command="{x:Static ApplicationCommands.Cut}" Content="Cut" /> <Button Command="{x:Static ApplicationCommands.Copy}" Content="Copy" /> <Button Command="{x:Static ApplicationCommands.Paste}" Content="Paste" /> </ToolBar> <ToolBar Band="1" BandIndex="1"> <ToolBarPanel Orientation="Vertical"> <Label Content="Dummy0" ToolBar.OverflowMode="AsNeeded" /> <Label Content="Dummy1" ToolBar.OverflowMode="AsNeeded" /> <Label Content="Dummy2" ToolBar.OverflowMode="AsNeeded" /> </ToolBarPanel> </ToolBar> </ToolBarTray> <StackPanel Orientation="Horizontal" DockPanel.Dock="Top" FocusManager.IsFocusScope="True"> <Button Command="{x:Static ApplicationCommands.Cut}" Content="Cut" /> <Button Command="{x:Static ApplicationCommands.Copy}" Content="Copy" /> <Button Command="{x:Static ApplicationCommands.Paste}" Content="Paste" /> <!-- changed --> <Button Command="c:PlaySound.PlaySoundCommand" Content="Play" CommandParameter="What a lovely song!" /> </StackPanel> <Grid> <Grid.ColumnDefinitions> <ColumnDefinition /> <ColumnDefinition Width="Auto"/> <ColumnDefinition /> </Grid.ColumnDefinitions> <TextBox TextWrapping="Wrap" Width="Auto" Grid.Column="0" Grid.ColumnSpan="1" Margin="0" >It happened that a Fox caught its tail in a trap, and in struggling to release himself lost all of it but the stump. At first he was ashamed to show himself among his fellow foxes.</TextBox> <GridSplitter Grid.Column="1" VerticalAlignment="Stretch" HorizontalAlignment="Stretch" Margin="1,0,0,0" Width="3"/> <TextBox TextWrapping="Wrap" Width="Auto" Grid.Column="2" Grid.ColumnSpan="1" Margin="0" >But at last he determined to put a bolder face upon his misfortune, and summoned all the foxes to a general meeting to consider a proposal which he had to place before them.</TextBox> </Grid> </DockPanel> </Window>
There is a shortcut to calling commands. You can create an ICommand instance and provide it via a property. The downside – what did you expect? – is that you have no shortcut key or any other comfort.
Step 1: Create a class that inherits from interface ICommand.
using System; using System.Windows.Input; namespace CustomCommands { public class PlaySound2 : ICommand { object _DependencyObject; public PlaySound2(object xDependencyObject) { _DependencyObject = xDependencyObject; } // constructor public event EventHandler CanExecuteChanged { add { CommandManager.RequerySuggested += value; } remove { CommandManager.RequerySuggested -= value; } } // public bool CanExecute(object xParameter) { return (DateTime.Now.Second % 2 == 0); // timer based example } // public void Execute(object xParameter) { //_DependencyObject.DoSomething(); System.Windows.MessageBox.Show("Parameter: " + xParameter.ToString()); System.Media.SystemSounds.Beep.Play(); } // } // class } // namespace
Step 2: instantiate that class and provide it via a property. You do not need to expose the class in your MainWindow. You can use any class. Set the DataContext to your class where the property is (or use a precise path that leads to that object).
using CustomCommands; using System; using System.Windows; using System.Windows.Input; namespace DemoApp { public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); SimpleExecution = new PlaySound2("dummy"); DataContext = this; } private void CommandBinding_PlaySound_Executed(object sender, ExecutedRoutedEventArgs e) { System.Media.SystemSounds.Beep.Play(); MessageBox.Show("Source: " + e.Source.ToString() + Environment.NewLine + "OriginalSource: " + e.OriginalSource.ToString() + Environment.NewLine + "Parameter: " + e.Parameter.ToString()); } // public ICommand SimpleExecution { get; private set; } } // class } // namespace
Step 3: Bind the command in XAML.
<Window x:Class="DemoApp.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:c="clr-namespace:CustomCommands" Title="MainWindow" Height="400" Width="400"> <Window.CommandBindings> <CommandBinding Command="c:PlaySound.PlaySoundCommand" Executed="CommandBinding_PlaySound_Executed" /> </Window.CommandBindings> <DockPanel LastChildFill="True" > <Menu DockPanel.Dock="Top"> <MenuItem Header="_Edit"> <MenuItem Command="{x:Static ApplicationCommands.Cut}" CommandParameter="Cut"/> <MenuItem Command="{x:Static ApplicationCommands.Copy}" CommandParameter="Copy"/> <MenuItem Command="{x:Static ApplicationCommands.Paste}" CommandParameter="Paste"/> </MenuItem> <MenuItem Header="_Media"> <MenuItem Command="c:PlaySound.PlaySoundCommand" CommandParameter="Play"/> </MenuItem> </Menu> <ToolBarTray Background="Gray" DockPanel.Dock="Top"> <ToolBar Band="0" BandIndex="0" > <Button Command="{x:Static ApplicationCommands.Cut}" Content="Cut" /> <Button Command="{x:Static ApplicationCommands.Copy}" Content="Copy" /> <Button Command="{x:Static ApplicationCommands.Paste}" Content="Paste" /> </ToolBar> <ToolBar Band="1" BandIndex="1"> <ToolBarPanel Orientation="Vertical"> <Label Content="Dummy0" ToolBar.OverflowMode="AsNeeded" /> <Label Content="Dummy1" ToolBar.OverflowMode="AsNeeded" /> <Label Content="Dummy2" ToolBar.OverflowMode="AsNeeded" /> </ToolBarPanel> </ToolBar> </ToolBarTray> <StackPanel Orientation="Horizontal" DockPanel.Dock="Top" FocusManager.IsFocusScope="True"> <Button Command="{x:Static ApplicationCommands.Cut}" Content="Cut" /> <Button Command="{x:Static ApplicationCommands.Copy}" Content="Copy" /> <Button Command="{x:Static ApplicationCommands.Paste}" Content="Paste" /> <Button Command="c:PlaySound.PlaySoundCommand" Content="Play" CommandParameter="What a lovely song!" /> <!-- changed --> <Button Command="{Binding SimpleExecution}" Content="StraightForward" CommandParameter="The German way!" /> </StackPanel> <Grid> <Grid.ColumnDefinitions> <ColumnDefinition /> <ColumnDefinition Width="Auto"/> <ColumnDefinition /> </Grid.ColumnDefinitions> <TextBox TextWrapping="Wrap" Width="Auto" Grid.Column="0" Grid.ColumnSpan="1" Margin="0" >It happened that a Fox caught its tail in a trap, and in struggling to release himself lost all of it but the stump. At first he was ashamed to show himself among his fellow foxes.</TextBox> <GridSplitter Grid.Column="1" VerticalAlignment="Stretch" HorizontalAlignment="Stretch" Margin="1,0,0,0" Width="3"/> <TextBox TextWrapping="Wrap" Width="Auto" Grid.Column="2" Grid.ColumnSpan="1" Margin="0" >But at last he determined to put a bolder face upon his misfortune, and summoned all the foxes to a general meeting to consider a proposal which he had to place before them.</TextBox> </Grid> </DockPanel> </Window>
That’s it for today 🙂
WPF Commands (part 1)
Due to personal time limitations I add a shorter post today. Nonetheless this post is vital for programmers without any WPF experience.
Events are fairly low-level and deprecated in WPF to a certain degree. They are against the idea of MVVM, which in simple terms is the separation of code and user interface. The new technology is task based – known as commands. Commands act like glue between the user interface and the code. You can avoid a lot of repetitive event-handling code. They deal with text captions and enabled/disabled state synchronizations. Let’s have a look at the ICommand interface:
namespace System.Windows.Input { public interface ICommand { event EventHandler CanExecuteChanged; bool CanExecute(object parameter); void Execute(object parameter); } // interface } // namespace
The interface ICommand is inherited by RoutedCommand and indirectly by RoutedUICommand. RoutedUICommand has an additional descriptive text property, which RoutedCommand does not have. Besides this they are the same. RoutedUICommand inherits from RoutedCommand and is used for commands that display some text in the UI like menu items or tooltips.
namespace System.Windows.Input { public class RoutedUICommand : RoutedCommand { public RoutedUICommand(); public RoutedUICommand(string text, string name, Type ownerType); public RoutedUICommand(string text, string name, Type ownerType, InputGestureCollection inputGestures); public string Text { get; set; } // Text that describes this command. } // class } // namespace
RoutedCommand is the only class in WPF that inherits from ICommand. All other classes are derived from RoutedCommand. An important feature of RoutedCommand in WPF is the so-called bubbling behavior. When you have a button in a StackPanel, then events will be executed in that order: Button -> StackPanel -> Window. I will keep it simple here and explain that behavior in another posts.
WPF has prebuild commands like Cut, Copy, Paste, Open and Print. There is no code behind these commands. You have to bind them to your code. Bubbling does play a big role here. You could press Ctrl+C to copy a text. The same command could be part of a TextBox and a window menu. The bubbling can be used to place the same command in two different places. The key input triggers the window command and provides enough information to process it.
Prebuild commands define a standard and generally increase code reusability.
Notice that RoutedCommand has its enhanced versions of CanExecute() and Execute():
namespace System.Windows.Input { public class RoutedCommand : RoutedCommand { public RoutedCommand(); public RoutedCommand(string name, Type ownerType); public RoutedCommand(string name, Type ownerType, InputGestureCollection inputGestures); public InputGestureCollection InputGestures { get; } public string Name { get; } public Type OwnerType { get; } public event EventHandler CanExecuteChanged; public bool CanExecute(object parameter, IInputElement target); public void Execute(object parameter, IInputElement target); } // class } // namespace
This is confusing. You naturally expect the same definition as in ICommand. The answer must be that the interface ICommand is implemented explicitly. Thus the interface method is hidden and can only be accessed indirectly via an interface variable. See below example:
public interface IMyInterface { void hello(object o); } // interface public class MyClass : IMyInterface { public void hello(object i, string s) { } void IMyInterface.hello(object o) {} } // class static void Main(string[] args) { MyClass x = new MyClass(); x.hello("abc", "abc"); // interface not accessible IMyInterface c = x; c.hello("abc"); // class not accessible }
There are several groups (static classes) of prebuild commands in WPF.
ApplicationCommands provides classical commands that are in most applications (eg. Cut, Copy, Paste, Save, New, Print).
NavigationCommands provide commands for navigation that you know from browsers (eg. BrowseForward, BrowseBack).
EditingCommands are known from text editors (eg. Delete, MoveToDocumentEnd, MoveRightByWord, DecreaseFontSize).
ComponentCommands are used to move the cursor around (some duplicates are in EditingCommands; eg. MoveDown, MoveFocusBack, ExtendSelectionDown).
MediaCommands are self-explanatory (eg. Play, Pause, Record, BoostBass, ChannelDown).
These classes are static, there can only one instance in your application. They all have default keystrokes. For instance Ctrl+C is predefined for Copy. When you bind them to a command source and add that command source to a window, then the key combination becomes active. You don’t even need a visible control. An additional feature is that key combinations of commands are automatically shown in menus.
We had some theory now. Let’s get practical:
<Window x:Class="DemoApp.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="MainWindow" Height="200" Width="200"> <StackPanel> <Button Command="{x:Static ApplicationCommands.New}" CommandParameter="NewProjectX" Content="New" FontSize="20" /> </StackPanel> </Window>
using System; using System.Windows; using System.Windows.Input; namespace DemoApp { public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); CommandBinding lCommandBinding = new CommandBinding(ApplicationCommands.New); lCommandBinding.Executed += Button_Pressed; CommandBindings.Add(lCommandBinding); // add the binding object to our main "window" instance } // private void Button_Pressed(object sender, ExecutedRoutedEventArgs e) { MessageBox.Show(sender.ToString() + " did send the message: " + e.Parameter.ToString() + Environment.NewLine + "OriginalSource: " + e.OriginalSource); } // } // class } // namespace
You don’t have to add the command in your C# code. You can add it directly in XAML. This can be more comfortable. Unfortunately there is a downside as well. The IntelliSense support suffers and I personally believe that it is more error-prone.
<Window x:Class="DemoApp.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="MainWindow" Height="200" Width="200"> <Window.CommandBindings> <CommandBinding Command="{x:Static ApplicationCommands.New}" Executed="Button_Pressed" /> </Window.CommandBindings> <StackPanel> <Button Command="{x:Static ApplicationCommands.New}" CommandParameter="NewProjectX" Content="New" FontSize="20" /> </StackPanel> </Window>
using System; using System.Windows; using System.Windows.Input; namespace DemoApp { public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); //CommandBinding lCommandBinding = new CommandBinding(ApplicationCommands.New); //lCommandBinding.Executed += Button_Pressed; //CommandBindings.Add(lCommandBinding); // add the binding object to the window } // private void Button_Pressed(object sender, ExecutedRoutedEventArgs e) { MessageBox.Show(sender.ToString() + " did send the message: " + e.Parameter.ToString() + Environment.NewLine + "OriginalSource: " + e.OriginalSource); } // } // class } // namespace
The next example adds a menu. The shortcut Ctrl+N is automatically shown. There is a slight change for the button as well. The object content (text) now binds to itself and uses the text from the command. The advantage is that you hardcode less and become more flexible on multi-language support later on.
You don’t have to change anything in the C# source code.
<Window x:Class="DemoApp.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="MainWindow" Height="200" Width="200"> <Window.CommandBindings> <CommandBinding Command="{x:Static ApplicationCommands.New}" Executed="Button_Pressed" /> </Window.CommandBindings> <StackPanel> <Menu> <MenuItem Header="_File"> <MenuItem Command="{x:Static ApplicationCommands.New}" CommandParameter="NewProject_Y"/> </MenuItem> </Menu> <Separator/> <Button Command="{x:Static ApplicationCommands.New}" CommandParameter="NewProject_X" Content="{Binding RelativeSource={RelativeSource Self}, Path=Command.Text}" FontSize="20" /> </StackPanel> </Window>
Final note: Do not use the old school WinForms like approach anymore:
<Button Click="Button_Click" CommandParameter="NewProject_C" FontSize="20">Click</Button>
private void Button_Click(object sender, RoutedEventArgs e) { MessageBox.Show("Button clicked"); }
The next post will follow-up on custom commands.