0

I'm learning WPF.

In one of the exercises, I have a TextBox and buttons Cut and Paste. The following is enough to implement Cut and Paste functionality:

XAML:

<DockPanel>
    <WrapPanel DockPanel.Dock="Top" Margin="3">
        <Button Command="ApplicationCommands.Cut"
                CommandTarget="{Binding ElementName=txtEditor}"
                Width="60">
                _Cut
        </Button>
        <Button Command="ApplicationCommands.Paste"
                CommandTarget="{Binding ElementName=txtEditor}"
                Width="60" Margin="3,0">
                _Paste<
        /Button>
    </WrapPanel>
    <TextBox AcceptsReturn="True" Name="txtEditor" />
    </DockPanel>

When pressed, the button Cut executes the ApplicationCommands.Cut on the TextBox with name txtEditor. When needed, the button will ask the TextBox with name textEditor if it can execute a Cut command, and when pressed it will order the textEditor to execute the Cut command.

Fairly straightforward. It works fine.

Just for Fun, I'd like to implement another button: Clear. When pressed it should clear the TextBox. The Textbox class has a method Clear.

<Button Command="ApplicationCommands.Clear"
                CommandTarget="{Binding ElementName=txtEditor}"
                Width="60">
                Clear
        </Button>

Alas, this won't work. ApplicationCommands doesn't have a Clear. Should I implement a custom command, as suggested in this example?

I tried the following:

I implemented CanExecute and Executed methods in my window:

public partial class CustomCommandSample : Window
{
    public CustomCommandSample()
    {
        InitializeComponent();
    }

    private void ClearCommand_CanExecute(object sender, CanExecuteRoutedEventArgs e)
    {
        e.CanExecute = true;
    }

    private void ClearCommand_Executed(object sender, ExecutedRoutedEventArgs e)
    {
        txtEditor.Clear();
    }
}

A static CustomCommands class:

public static class CustomCommands
{
    public static RoutedUICommand Clear => new RoutedUICommand (
        "Clear",
        "Clear",
        typeof(CustomCommands));
}

Finally the XAML: (Note: the classes in this project are in namespace WpfCommandDemo. Xaml refers to it as Local)

<Window x:Class="WpfTutorialSamples.Commands.UsingCommandsSample"
    xmlns="...
    xmlns:local="clr-namespace:WpfCommandDemo"
    Title="UsingCommandsSample" Height="100" Width="200">

    <Window.CommandBindings>
        <CommandBinding Command="CustomCommands.Clear"
                        CanExecute="ClearCommand_CanExecute"
                        Executed="ClearCommand_Executed" />
    </Window.CommandBindings>

 <DockPanel>
    <WrapPanel DockPanel.Dock="Top" Margin="3">
        <Button Command="CustomCommands.Clear"
                CommandTarget="{Binding ElementName=txtEditor}"
                Width="60">
                Clear
        </Button>
        ... (other buttons: cut / paste, as above
    </WrapPanel>
        <TextBox AcceptsReturn="True" Name="txtEditor" />
    </DockPanel>

Although this compiles, The constructor of CustomCommandSample throws an XamlParseException:

Type reference cannot find type named 
'{http://schemas.microsoft.com/winfx/2006/xaml/presentation}CustomCommands'.

Should I solve the problem using Custom Commands? What should I change? Or am I completely wrong, and should I solve this differently

1
  • 1
    You can implement your own relay command for that, have a look at this thread Commented Jan 13, 2020 at 10:45

2 Answers 2

1

Should I solve the problem using Custom Commands?

Yes. This is how to solve this using the Model-View-ViewModel (MVVM) design pattern which is the recommended design pattern to use when developing XAML based UI applications.

From this blog post:

WPF provides two implementations of the ICommand interface; the System.Windows.Input.RoutedCommand and System.Windows.Input.RoutedUICommand where the latter is a subclass of the former that simply adds a Text property that describes the command. However, neither of these implementations are especially suited to be used in a view model as they search the visual tree from the focused element and up for an element that has a matching System.Windows.Input.CommandBinding object in its CommandBindings collection and then executes the Execute delegate for this particular CommandBinding. Since the command logic should reside in the view model, you don’t want to setup a CommandBinding in the view in order to connect the command to a visual element. Instead, you can create your own command by creating a class that implements the ICommand. The below implementation is a common one that invokes delegates for the Execute and CanExecute methods:

public class DelegateCommand: System.Windows.Input.ICommand
{
    private readonly Predicate<object> _canExecute;
    private readonly Action<object> _execute;

    public DelegateCommand(Action<object> execute)
        : this(execute, null) { }

    public DelegateCommand(Action<object> execute, Predicate<object> canExecute)
    {
        _execute = execute;
        _canExecute = canExecute;
    }

    public bool CanExecute(object parameter) => _canExecute == null ? true : _canExecute(parameter);

    public void Execute(object parameter) => _execute(parameter);

    public event EventHandler CanExecuteChanged;

    public void RaiseCanExecuteChanged() => CanExecuteChanged?.Invoke(this, EventArgs.Empty);
}

Once you have an implementation of the ICommand interface, it's easy to use in your view models:

public class ViewModel : INotifyPropertyChanged
{
    public ViewModel()
    {
        ClearCommand = new DelegateCommand(Clear);
    }

    private string _text;
    public string Text
    {
        get { return _text; }
        set { _text = value; NotifyPropertyChanged(); }
    }

    public ICommand ClearCommand { get; }

    private void Clear(object parameter)
    {
        Text = string.Empty;
    }

    public event PropertyChangedEventHandler PropertyChanged;
    private void NotifyPropertyChanged([CallerMemberName] string propertyName = "") =>
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}

In the view, you simply bind to the properties of the view model:

<TextBox AcceptsReturn="True" Name="txtEditor" Text="{Binding Text}" />
<Button Content="Clear" Command="{Binding ClearCommand}" />

Just remember to set the DataContext of the view to an instance of your view model for the bindings to work:

public MainWindow()
{
    InitializeComponent();
    DataContext = new ViewModel();
}
Sign up to request clarification or add additional context in comments.

Comments

1

To use CustomCommands in XAML, you'll need to add a reference to it. In the element, add a line:

xmlns:custom="clr-namespace:MyApplication.NamespaceWithCustomInIt"

Replacing the namespace value as appropriate. Then you should be able to reference CustomCommands anywhere in XAML as custom:CustomCommands (may have to bind, I'll check later).

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.