views:

499

answers:

5

In my ViewModel I have two commands:

ICommand ExecuteMeOnCheck { get; }
ICommand ExecuteMeOnUncheck { get; }

I wish to attach these commands to a CheckBox and have one of them execute when it is checked and the other execute when it is unchecked. What's the best way to achieve this without cluttering the View with code-behind?

A: 

well you can inherit from the checkBox control, add the two commands as properties (Dependency Propertiess) and then override the OnChecked and OnUnchecked methods and trigger each command in the appropriate place

abdelkrimnet
+3  A: 

The simplest way I have found to do this involves borrowing a bunch of code. I also think that you do not want to expose two events for the state change. Make your command CheckedChangedCommand instead of Checked and Unchecked. Bind the CheckBox.IsChecked dependency property to a property on your ViewModelClass. That way when your command gets executed, you just check that value in your ViewModel.

First, start using DelegateCommand from the WPF ViewModel toolkit. It isn't clear from your post whether you are or not.

Next, download the .zip archive from the Downloads tab at this link.

The archive has a class called EventBehaviourFactory that makes setting the DependencyProperty easier. Back on the Home tab at the above link, Samuel tells you how to wire up the Command in your XAML to the ViewModel. Here it is:

<TextBox ff:TextBoxBehaviour.TextChangedCommand="{Binding TextChanged}"/>

I used the three classes (DelegateCommand, EventBehaviourFactory and TextBoxBehaviour) line for line and had the TextChanged event working in my project in about five minutes.

The pattern for extending it to other controls like you need to do is easy too. I managed to wire up ListBox.SelectionChanged the same way. Create a new ControlBehaviour class and substitute "Control" for "TextBox" in all the instances and attach the DependencyProperty to the new event.

It is a beautiful thing when your code behind is empty!

Eddie Butt
Thank you very much for this hint. I ran into the above described problem about 3 hours ago and finally found this and it is exactly what I needed.I was already afraid, that I would have to part with the beautiful purity of putting all the logic into the model.The only solution I could think of was to route the events to the model from the View, but that wouldn't have been even half as nice as doing it this way.
Thorsten Lorenz
+1  A: 

Another solution, similar to Eddie's : the Attached Command Behaviours by Marlon Grech

You could also use the CheckBox's Command property to attach only one command, in which you test whether the checkbox is checked...

Or you could bind the IsChecked property to a property of your ViewModel, and react to the change in the setter of that property.

Thomas Levesque
Using a single command doesn't seem like a good idea because it introduces a dependency between the command and the CheckBox.Binding IsChecked would be an excellent solution in cases where commands aren't necessary.
GraemeF
+1  A: 

This relates to Eddie's answer.

I played around with it a bit and it is great I also created a little template for creating new commands. I'm using it with Sharpdevelop as a code snippet. Just paste the following into your code (e.g.via a snippet):

#region $nameCommand

    public static readonly DependencyProperty $nameCommand =
        EventBehaviourFactory.CreateCommandExecutionEventBehaviour(
            Control.$nameEvent, "$nameCommand", typeof (ControlBehavior));

    public static void Set$nameCommand(DependencyObject o, ICommand value)
    {
        o.SetValue($nameCommand, value);
    }

    public static ICommand Get$nameCommand(DependencyObject o)
    {
        return o.GetValue($nameCommand) as ICommand;
    }

    #endregion

Then do a search replace for "$name" with the name of the Event/Command e.g. MouseEnter

ote, it is importand, that you make sure you pick the right classname for the owner (in my case ControlBehavior.

I am attaching a class I created for common Mouse commands in the hopes that other people share as well and we don't just implement the same thing over and over, where we could have saved a lot of time by sharing. Here my complete ControlBehavior class (I did only implement the events that I needed so far):

public static class ControlBehavior
    {
        #region MouseEnterCommand

        public static readonly DependencyProperty MouseEnterCommand =
            EventBehaviourFactory.CreateCommandExecutionEventBehaviour(
                Control.MouseEnterEvent, "MouseEnterCommand", typeof (ControlBehavior));

        public static void SetMouseEnterCommand(DependencyObject o, ICommand value)
        {
            o.SetValue(MouseEnterCommand, value);
        }

        public static ICommand GetMouseEnterCommand(DependencyObject o)
        {
            return o.GetValue(MouseEnterCommand) as ICommand;
        }

        #endregion

        #region MouseLeaveCommand

        public static readonly DependencyProperty MouseLeaveCommand =
            EventBehaviourFactory.CreateCommandExecutionEventBehaviour(
                Control.MouseLeaveEvent, "MouseLeaveCommand", typeof (ControlBehavior));

        public static void SetMouseLeaveCommand(DependencyObject o, ICommand value)
        {
            o.SetValue(MouseLeaveCommand, value);
        }

        public static ICommand GetMouseLeaveCommand(DependencyObject o)
        {
            return o.GetValue(MouseLeaveCommand) as ICommand;
        }

        #endregion


        #region MouseDoubleClickCommand

        public static readonly DependencyProperty MouseDoubleClickCommand = 
            EventBehaviourFactory.CreateCommandExecutionEventBehaviour(
            Control.MouseDoubleClickEvent, "MouseDoubleClickCommand", typeof (ControlBehavior));

        public static void SetMouseDoubleClickCommand(Control o, ICommand command)
        {
            o.SetValue(MouseDoubleClickCommand, command);
        }

        public static void GetMouseDoubleClickCommand(Control o)
        {
            o.GetValue(MouseDoubleClickCommand);
        }

        #endregion

        #region MouseLeftButtonDownCommand

        public static readonly DependencyProperty MouseLeftButtonDownCommand =
            EventBehaviourFactory.CreateCommandExecutionEventBehaviour(
                Control.MouseLeftButtonDownEvent, "MouseLeftButtonDownCommand", typeof (ControlBehavior));

        public static void SetMouseLeftButtonDownCommand(DependencyObject o, ICommand value)
        {
            o.SetValue(MouseLeftButtonDownCommand, value);
        }

        public static ICommand GetMouseLeftButtonDownCommand(DependencyObject o)
        {
            return o.GetValue(MouseLeftButtonDownCommand) as ICommand;
        }

        #endregion

    }

By using above template it took me only a few seconds for each command.

Thorsten Lorenz
A: 

I did this with a Trigger on "IsChecked" that sets the Command.

e.g.

<Style x:Key="checkBoxStyle" TargetType="{x:Type CheckBox}">
      <Style.Triggers>
        <Trigger Property="IsChecked" Value="True">
          <Setter Property="Command"
                  Value="{Binding AddThingCommand}" />
        </Trigger>
        <Trigger Property="IsChecked" Value="False">
          <Setter Property="Command"
                  Value="{Binding RemoveThingCommand}" />
        </Trigger>
      </Style.Triggers>
    </Style>
Timje