Click here to Skip to main content
Click here to Skip to main content

WPF Commands Return Value

, 7 Aug 2008
Rate this:
Please Sign up or sign in to vote.
Allows WPF commands to interact back with the command sources
main_play.pngmain_pause.png

Introduction

This issue suggests an implementation of extending WPF routed commands in a way that enables them interaction back with the command sources for synchronization purposes.

Background

At first glance, WPF commands looked like… sort of events, that's what I thought, till I read a bit more about them. WPF commands are a great way to separate the command logic from its trigger sources (command sources, the ones that invoke the commands) and customize different logic on various targets. Routed command bubbles and tunnels through the element tree allowing each element to define its ability to execute the command and execute it when all the proper conditions add up.

One of its big strengths are a set of ready to use commands which have a built in support as part of some of the controls for performing specific command functionality corresponding to the element it is occurring on.

While examining and using WPF commands, along with its benefits, its deficiencies started to come out.

The Problem

One main shortage and the topic of our article is the inability of the command source to get updates from the command itself, it just wasn't handled by Microsoft.

Let's use an example to clear things up. Take a simple player application for example, which allows the user to play a media file by either clicking a play/pause toggle button or selecting a play/pause entry from a menu bar. Since both scenarios run the same functionality, both controls hook up the same command which get handled in the same target, the player form in this case, which is also the visual parent of both controls. The first scenario of clicking the toggle button works like a charm. But in the second scenario of clicking the menu entry "Play/Pause" is a whole other story. While the media file plays the toggle button stays apathetic to the new condition. Not only that, WPF commands suggest no support for such a scenario. It doesn't allow any kind of back door to the command sources. This article addresses the aforementioned WPF commands deficiency and suggests an appropriate solution for most uses.

Solutions

There are several ways of solving/bypassing the problem. Let's review two main solutions which solve our problem while maintaining a loosely coupled UI.

1. Use Our Own Class as a Command Parameter

A known and spoken solution proposes sending a custom object as the command property which comprises a value for the command sources which actuated the command to use as a return value. After each concrete command execution, all concrete command sources compel updating. Further information on that approach can be found at Extending WPF Commands by Patrick Klug.

Just to point out the main pros and cons for comparison:

Pros
  • Solves the problem
  • Keeps the opportunity of using WPF supported commands
Cons
  • Forces customization of each and every single control that functions as a command source
  • Causes superfluous CanExecute method invocations (one for its official part in WPF commanding mechanism and the other for the spoken solution)

2. Customize RoutedCommand to Embrace a Return Value

This solution suggests a custom command that leverages the existing one in a way that enables a return value per command, meaning that each source control attached to a concrete custom command can be notified on changes and access the return value to refresh itself.

Pros
  • Solves the problem
  • Does not require custom controls
Cons
  • Eliminates the ability to use WPF supported command
  • Since commands are static, you can have only one return value per command. Can be overcome by using a custom object (maybe some kind of a collection) as the return value. But this is an infrequent scenario and won't be addressed on this issue.

The Solution

Since originally I used the propose design as a solution to a player we developed at work, I decided to exemplify it on a player common scenario, playing a media file (a virtual one Smile | :) )from either a play/pause toggle button or a play/pause menu entry. As opposed to the toggle button which changes its state correspondingly playing or paused, the menu stays as is regardless of the media file state.

main_play_mouse.pngmain_pause_mouse.pngmain_menu_mouse.png

Actually I use it on numerous and disparate scenarios using all kinds of command source controls combination involving two or more of them.

Like almost everything in life, this solution comprises three parts:

  1. Creating the custom command
  2. Making sure all command source controls give ear to return value changes
  3. Applying the return value to the command on command handling

And you're ready to go. Yes, it's that simple!!!

Creating the Custom Command

The engine that runs this solution is the CustomCommand class which inherits the RoutedCommand WPF class.

public class CustomCommand : RoutedCommand
    {
        public delegate void ReturnValueChangedHandler(object sender, EventArgs e);
        //an event for command sources on return value change
        public event ReturnValueChangedHandler ReturnValueChanged;

        private object returnValue;

        public CustomCommand(string Name, Type OnerType): base(Name, OnerType)
        {
        }

        //the return value
        public object ReturnValue
        {
            get
            {
                return returnValue;
            }
            set
            {
                returnValue = value;
                ReturnValueChanged(this, new EventArgs());
            }
        }
    }

The idea is to expand the command so it would carry a return value along with it aka ReturnValue and a ReturnValueChanged event to notify to whoever wants to listen, in fact to the command sources that rely on it. As you can see, that event raises on each ReturnValue set.

Make Sure All Command Source Controls Give Ear to Return Value Changes

Since in our example, the toggle button is the only command source that needs the command return value, we'll see just the following code snippet:

//listening to ReturnValueChanged event and setting the
//toggle button state correspondingly
((CustomCommand) Commands.Commands.ToggleCommand).ReturnValueChanged += (sender, e) =>
{
   toggleBtn.IsChecked =
	(bool)((CustomCommand)Commands.Commands.ToggleCommand).ReturnValue;
};

Setting the Command Return Value on Command Handling

The only thing left is setting the appropriate return value on command execution.

private void CommandBinding_Executed(object sender, ExecutedRoutedEventArgs e)
{
    //setting the return value
    //in the real world we probably won't have to hold a state flag
    //"isPlay" we'll figure that out as part of the alg
    if (e.Parameter == null)
       //the command came from menu
       ((CustomCommand)e.Command).ReturnValue = isPlay = !isPlay;
    else
       //the command came from toggle button
       ((CustomCommand) e.Command).ReturnValue = isPlay = (bool)e.Parameter;

       // TODO: insert command logic here

       e.Handled = true;
}

Conclusion

As you can see, after only three simple steps you have the whole commanding return value mechanism ready to go.

Attached is a demo app for clarifications (VS 2008 project).

History

  • 8th August, 2008: Initial post

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Kobi Udi
Software Developer
Israel Israel
No Biography provided

Comments and Discussions

 
GeneralNeed to convert this line of your code... Pinmemberinsatiable18-Nov-08 4:20 
GeneralRe: Need to convert this line of your code... PinmemberKobi Udi19-Nov-08 2:47 
Just use a delegate, ReturnValueChanged is an event so just assign a delagate to it that calls a function that runs this line of code:
toggleBtn.IsChecked =(bool)((CustomCommand)Commands.Commands.ToggleCommand).ReturnValue;
 
hope it helped
GeneralRe: Need to convert this line of your code... PinmemberKobi Udi19-Nov-08 2:52 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web01 | 2.8.140821.2 | Last Updated 8 Aug 2008
Article Copyright 2008 by Kobi Udi
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid