2

Is there a way to create "Instance only" ICommand implementation for Custom Control, WITHOUT static classes "behind"?

I am trying to update previously created custom control.

One of the goals is to ensure multiinstance capability.

If two or more instances of the same custom control was used in the same application, there is (as expected) interferences from any static clases that are used behind.

I figured out how to get rid of the most, but having troubles with ICommand.

Given GUI items on the Custom Control have Command that must be only valid within the User Control instance - instead of this now the Command is interfering with all instances (as example CanExecute makes GUI items active on UserControl Instances where the "local" conditions are not met).

Euphoric
  • 12,645
  • 1
  • 30
  • 44
user1608721
  • 136
  • 1
  • 9
  • 1
    I think the problems will be similar to Dependency Properties: http://stackoverflow.com/questions/3660696/non-static-dependency-properties – Matěj Zábský Jun 06 '13 at 08:41
  • 1
    I think you don't understand commands correctly. There is nothing that makes commands related to static classes. – Euphoric Jun 06 '13 at 08:43
  • @Euphoric: MSDN documentation, related to commands, really sucks, because it is full of such kind of samples (when commands are in static class). At the same time, MVVM approach, which is mainstream for WPF, assumes, that command is a part of view model. I understand, why this question has appeared. – Dennis Jun 06 '13 at 08:56

3 Answers3

2

You can create your command and expose it as a property of your ViewModel, then bind to it in your control:

In your ViewModel:

public ICommand MyCommand {get;set;} // construct your command and set it here

in your control:

<Button Command="{Binding MyCommand}"/>

if you are not using MVVM pattern, then you should create the same field in your DataContext (probably in your controls code behind)

you can also use Dependency properties in order to define your command, if you change your command after your user control is created, you should use it.

In general:

In order to know your options when writing in WPF / C# I recommend reading about MVVM pattern, dependency properties, DataContext and Binding - you may know some of this already.

Ron.B.I
  • 2,726
  • 1
  • 20
  • 27
  • I'd say "expose it as as property" instead of "save it in a field", because data binding doesn't work with fields. This may be confusing. – Dennis Jun 06 '13 at 08:48
  • Thank you for your insight, English is not my native tongue and your comment is much appreciated - I've changed it. – Ron.B.I Jun 06 '13 at 09:28
1

I think you might be confused by the fact that the CanExecute and Execute methods do not have a parameter linking them to the object upon which they are supposed to act.

But remember that the ICommand interface must be implemented by a class, and objects of that class can and should have fields, typically initialized in the constructor.

For example, if you follow the MVVM pattern (as already mentioned by Ron.B.I.), the command typically has a reference to the viewmodel. Or you can use something like a RelayCommand, and capture the viewmodel in a delegate or lambda closure object.

Kris Vandermotten
  • 10,111
  • 38
  • 49
1

Thank you very much for the answers and clarifications!

You gave me the all deciding kick so I figured it out. I added my examples complete on purpouse.

Following your advices (Ron B I & Dennis) I first wanted read more about the ViewModel.

Under http://msdn.microsoft.com/en-ca/magazine/dd419663.aspx there are examples with non-static class behind. So the solution was simply to add new class in my user control (Exactly as shown on the mentioned site - Figure 3 - some name changed - Copyright belongs to Josh Smith joshsmithonwpf.wordpress.com ):

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Input;

namespace WpfCommandControl
{
class CommandImplementation : ICommand
{
    #region Fields

    readonly Action<object> _execute;
    readonly Predicate<object> _canExecute;

    #endregion // Fields

    #region Constructors

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

    public CommandImplementation(Action<object> execute, Predicate<object> canExecute)
    {
        if (execute == null)
            throw new ArgumentNullException("execute");

        _execute = execute;
        _canExecute = canExecute;
    }
    #endregion // Constructors

    #region ICommand Members

    public bool CanExecute(object parameter)
    {
        return _canExecute == null ? true : _canExecute(parameter);
    }

    public event EventHandler CanExecuteChanged
    {
        add { CommandManager.RequerySuggested += value; }
        remove { CommandManager.RequerySuggested -= value; }
    }

    public void Execute(object parameter)
    {
        _execute(parameter);
    }

    #endregion // ICommand Members

}
}

Then in the User Control "Window"

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.ComponentModel;

namespace WpfCommandControl
{
    public partial class CommandControl : UserControl, INotifyPropertyChanged
    {
    #region [ Private Members ]
    private bool _canActivated = false;
    private int _counter = 0;
    CommandImplementation _activateCommand;

    #endregion

    #region [ Properties ]
    public int CommandCounter
    {
        get
        {
            return _counter;
        }

        set
        {
            _counter = value;
            OnNotifyPropertyChanged("CommandCounter");
        }

    }

    public bool CanActivated
    {
        get
        {
            return _canActivated;
        }

        set
        {
            _canActivated = value;
            OnNotifyPropertyChanged("CanActivated");
        }        
    }
    #endregion

    #region [ Property_Changed_Utilities ]
    public event PropertyChangedEventHandler PropertyChanged;

    private void OnNotifyPropertyChanged(String info)
    {
        // Note: Do not forget to add interface "INotifyPropertyChanged" to your class.
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(info));
        }
    }
    #endregion

    # region [ Commands ]
    public ICommand ActivateCommand
    {
        get
        {
            return _activateCommand;
        }
    }
    #endregion

    #region [ Constructor ]
    public CommandControl()
    {
        InitializeComponent();
        _activateCommand = new CommandImplementation(param => this.Activate(), param => this.CanActivated);
    }
    #endregion

    #region [ Methods ]
    void Activate()
    {
        CommandCounter++;
    }
    #endregion


}
}

Most important part:

Command is implemented as Property:

    public ICommand ActivateCommand
    {
        get
        {
            return _activateCommand;
        }
    }

So it makes sure it will return the actual instance related Command, that was instantiated with Lambda-Expression in the User Control's constructor:

    public CommandControl()
    {
        InitializeComponent();
        _activateCommand = new CommandImplementation(param => this.Activate(), param => this.CanActivated);
    }

The Lambda - Expression makes the connection to the logic begind:

param => this.Activate()

For the Activate() function wich will be executed as Command is fired

    void Activate()
    {
        CommandCounter++;
    }

And

param => this.CanActivated

For passing the local logic behind for the ICommand CanExecute Property, thus giving you control on when the command can be executed.

In my case I used property that can be bind to the CheckBox, but you can also do it another way...

    public bool CanActivated
    {
        get
        {
            return _canActivated;
        }

        set
        {
            _canActivated = value;
            OnNotifyPropertyChanged("CanActivated");
        }        
    }

Again it is as shown from Josh Smith joshsmithonwpf.wordpress.com - I just changed it to instantiate in the constructor instead of check if the private member is null and delivering new instance if needed in the GET part of the Command Property.

Rest of the code is just implementation of needed Properties and OnNotifyPropertyChanged as shown on MSDN.

XAML is simple - just for the proof of concept.

<UserControl x:Class="WpfCommandControl.CommandControl"
         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:local="clr-namespace:WpfCommandControl"
         xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
         d:DesignHeight="300"
         d:DesignWidth="300"
         mc:Ignorable="d">
<Grid>
    <StackPanel>
        <CheckBox Content="Activate" IsChecked="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=UserControl}, Path=CanActivated}" />
        <Button Command="{Binding RelativeSource={RelativeSource Mode=FindAncestor,
                                                                 AncestorType=UserControl},
                                  Path=ActivateCommand}"
                Content="Click me"
                IsEnabled="{Binding RelativeSource={RelativeSource Mode=FindAncestor,
                                                                   AncestorType=UserControl},
                                    Path=CanActivated}" />
        <Label Content="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=UserControl}, Path=CommandCounter}" IsEnabled="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=UserControl}, Path=CanActivated}" />
    </StackPanel>
</Grid>

As you can see there is only a CheckBox - Binding will provide Enable/Disable of Button. Click on Button fires the Command that simply increment counter - shown on the Label again trough binding.

Putting all together:

Just one simple XAML Form with four User Controls:

<Window x:Class="CommandsTest.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:CommandsTest"
    xmlns:uctrl="clr-namespace:WpfCommandControl;assembly=WpfCommandControl"
    Title="MainWindow"
    Width="525"
    Height="350">

<Grid>
    <Grid.RowDefinitions>
        <RowDefinition />
        <RowDefinition />
    </Grid.RowDefinitions>

    <Grid.ColumnDefinitions>
        <ColumnDefinition />
        <ColumnDefinition />
    </Grid.ColumnDefinitions>

    <uctrl:CommandControl Grid.Row="0" Grid.Column="0" />

    <uctrl:CommandControl Grid.Row="0" Grid.Column="1" />

    <uctrl:CommandControl Grid.Row="1" Grid.Column="0" />

    <uctrl:CommandControl Grid.Row="1" Grid.Column="1" />

</Grid>

Firing the commands on every control is exactly as needed inside the element.

All is solved in the WPF way - using Commands and Bindings without any direct interaction with GUI elements, thus the GUI can be exchanged without need of updates in the code behind.

Once again thank you for showing me that there is also another (instance safe) way to implement custom commands in WPF.

user1608721
  • 136
  • 1
  • 9