WPF Commands CanExecute


In the previous post we implemented a CanExecute event that simply returned true so that the button would be available all the time. However, this is of course not true for all buttons – in many cases, you want the button to be enabled or disabled depending on some sort of state in your application. A very common example of this is the toggling of buttons for using the Windows Clipboard, where you want the Cut and Copy buttons to be enabled only when text is selected, and the Paste button to only be enabled when text is present in the clipboard. To see this example, go to WPF Tutorial.

I followed along with their example and made a few changes to the code. I added two new buttons: Wrap and Unwrap. As for the Copy and Paste, you don’t have to call these methods to have your buttons updated – WPF does it automatically when the application has an idle moment, making sure that your interface remains updated all the time. I tested this and copied something to the clipboard. The paste button was activated. I then used the Windows settings to clear the clipboard data. Automatically the Paste button was disabled. I didn’t have to write the code for that!

If we use built-in bindings, we can change our XAML and remove the code for the Cut and Paste buttons. That’s better and is illustrated in the next post called WPF Controls with Built-In Command Bindings.

Below is the XAML code.

<Window x:Class="CommandsCanExecuteCutPaste.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"
        xmlns:local="clr-namespace:CommandsCanExecuteCutPaste"
        mc:Ignorable="d"
        WindowStartupLocation="CenterScreen"
        Title="CommandsCanExecuteCutPaste" Height="200" Width="360">
    <Window.CommandBindings>
        <CommandBinding Command="ApplicationCommands.Cut" CanExecute="CutCommand_CanExecute" Executed="CutCommand_Executed" />
        <CommandBinding Command="ApplicationCommands.Paste" CanExecute="PasteCommand_CanExecute" Executed="PasteCommand_Executed" />
    </Window.CommandBindings>
    <DockPanel>
        <WrapPanel DockPanel.Dock="Top" Margin="3">
            <Button Command="ApplicationCommands.Cut" Width="60">_Cut</Button>
            <Button Command="ApplicationCommands.Paste" Width="60" Margin="3,0">_Paste</Button>
        </WrapPanel>
        <WrapPanel DockPanel.Dock="Bottom" Margin="3">
            <Button Name="WrapProperty" Width="60" Click="WrapProperty_Click">Wrap</Button>
            <Button Name="NoWrapProperty" Width="60" Margin="3,0" Click="NoWrapProperty_Click">No Wrap</Button>
        </WrapPanel>
        <TextBox AcceptsReturn="True" AcceptsTab="True" Name="txtEditor" TextWrapping="Wrap" />
    </DockPanel>
</Window>

Here is the code behind.

using System.Windows;
using System.Windows.Input;

namespace CommandsCanExecuteCutPaste
{ 
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }
        private void CutCommand_CanExecute(object sender, CanExecuteRoutedEventArgs e)
        {
            e.CanExecute = (txtEditor != null) && (txtEditor.SelectionLength > 0);
        }
        private void CutCommand_Executed(object sender, ExecutedRoutedEventArgs e)
        {
            txtEditor.Cut();
        }
        private void PasteCommand_CanExecute(object sender, CanExecuteRoutedEventArgs e)
        {
            e.CanExecute = Clipboard.ContainsText();
        }
        private void PasteCommand_Executed(object sender, ExecutedRoutedEventArgs e)
        {
            txtEditor.Paste();
        }
        private void WrapProperty_Click(object sender, RoutedEventArgs e)
        {
            txtEditor.TextWrapping = TextWrapping.Wrap;
        }
        private void NoWrapProperty_Click(object sender, RoutedEventArgs e)
        {
            txtEditor.TextWrapping = TextWrapping.NoWrap;
        }
    }
}