Partager via


SYSK 346: How WPF Commands Make Programmer’s Life Easier

Yesterday, I came across the following example demonstrating the power of WPF commands written by Adam Nathan and published in his book “Windows Presentation Foundation Unleashed” (I’ve made some small formatting changes):

 

<Window x:Class="WPFWinApp.Window1"

    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

    Title="WPFWinApp" Height="300" Width="600"

    >

      <StackPanel>

            <StackPanel Orientation="Horizontal">

                  <Button Command="Cut" CommandTarget="{Binding ElementName=tb1}" Content="{Binding RelativeSource={RelativeSource Self}, Path=Command.Text}" />

                  <Button Command="Copy" CommandTarget="{Binding ElementName=tb1}" Content="{Binding RelativeSource={RelativeSource Self}, Path=Command.Text}" />

                  <Button Command="Paste" CommandTarget="{Binding ElementName=tb1}" Content="{Binding RelativeSource={RelativeSource Self}, Path=Command.Text}" />

                  <Button Command="Undo" CommandTarget="{Binding ElementName=tb1}" Content="{Binding RelativeSource={RelativeSource Self}, Path=Command.Text}" />

                  <Button Command="Redo" CommandTarget="{Binding ElementName=tb1}" Content="{Binding RelativeSource={RelativeSource Self}, Path=Command.Text}" />

            </StackPanel>

            <StackPanel Orientation="Horizontal">

                  <Label>Enter some text: </Label>

                  <TextBox x:Name="tb1" Width="200" />

            </StackPanel>

      </StackPanel>

</Window>

 

Believe it or not, that’s all that’s needed to implement cut, copy, paste, undo and redo operations in WPF, including the buttons enabling/disabling themselves as needed (e.g. when nothing in clipboard, or when text not selected)! No code behind needed!

 

Try it for yourself! It’s pretty cool…

 

WPF defines a large number of commands – see ApplicationCommands, ComponentCommands, MediaCommands, NavigationCommands and EditingCommands – that allow developers to achieve rich interaction between controls that have no direct knowledge of each other (e.g. button and text box).

 

If you want to create your own commands, you’ll need to either implement ICommand interface or derive from RountedUICommand/RoutedCommand class. Or easier yet, just instantiate the RoutedUICommand class as a static field and provide get property to get its reference.

 

The UI element that wants to use your command would then set its Command property to your command’s name, set the Content property as appropriate and provide delegates for CanExecute and Executed events.

 

Here is the code sample that demonstrates that (either click on the ‘Click Me’ button or press Ctrl+Space. Note, the logic in the CanExecute delegate only enables the command when there is some text in textbox tb1.

 

XAML:

 

<Window x:Class="WPFWinApp.Window1"

    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

    xmlns:l = "clr-namespace:WPFWinApp"

    Title="WPFWinApp" Height="300" Width="600"

    >

      <StackPanel>

            <StackPanel Orientation="Horizontal">

                  <Button Command="Cut" CommandTarget="{Binding ElementName=tb1}" Content="{Binding RelativeSource={RelativeSource Self}, Path=Command.Text}" />

                  <Button Command="Copy" CommandTarget="{Binding ElementName=tb1}" Content="{Binding RelativeSource={RelativeSource Self}, Path=Command.Text}" />

                  <Button Command="Paste" CommandTarget="{Binding ElementName=tb1}" Content="{Binding RelativeSource={RelativeSource Self}, Path=Command.Text}" />

                  <Button Command="Undo" CommandTarget="{Binding ElementName=tb1}" Content="{Binding RelativeSource={RelativeSource Self}, Path=Command.Text}" />

                  <Button Command="Redo" CommandTarget="{Binding ElementName=tb1}" Content="{Binding RelativeSource={RelativeSource Self}, Path=Command.Text}" />

            </StackPanel>

            <StackPanel Orientation="Horizontal">

                  <Label>Enter some text: </Label>

                  <TextBox x:Name="tb1" Width="200" />

            </StackPanel>

            < StackPanelOrientation = "Horizontal">

                  < ButtonCommand = "l:Window1.MyCommand"Content="Click Me"Width="70" />

            </ StackPanel >

      </StackPanel>

     

      < Window.CommandBindings >

            < CommandBindingCommand = "l:Window1.MyCommand"CanExecute="MyCommand_CanExecute"Executed="MyCommand_Executed" />

      </ Window.CommandBindings >

      < Window.InputBindings >

            < KeyBindingCommand = "l:Window1.MyCommand"Key="Space"Modifiers="Control" />

      </ Window.InputBindings >

</Window>

 

 

Code-Behind:

 

using System;

namespace WPFWinApp

{

    public partial class Window1 : System.Windows.Window

    {

        private static System.Windows.Input.RoutedUICommand _myCommand =

            new System.Windows.Input.RoutedUICommand("Click Me", "MyCommand", typeof(System.Windows.Window));

        public static System.Windows.Input.RoutedUICommand MyCommand

        {

            get { return _myCommand; }

        }

        public Window1()

        {

      InitializeComponent();

        }

       

        void MyCommand_CanExecute(object sender, System.Windows.Input.CanExecuteRoutedEventArgs e)

        {

            if (string.IsNullOrEmpty(this.tb1.Text))

                e.CanExecute = false;

        else

                e.CanExecute = true;

        }

        void MyCommand_Executed(object sender, System.Windows.Input.ExecutedRoutedEventArgs e)

        {

            System.Diagnostics.Debug.WriteLine("MyCommand_Executed");

        }

    }

}