Extending a XamRibbon ButtonTool to execute ICommands

[Infragistics] Devin Rader / Tuesday, July 12, 2011

Infragistics Silverlight Ribbon control does not currently expose a Command and CommandParameter properties on its ButtonTool class.  Our guidance to customers wanting to use commands with this tool has been to use the Infragistics commanding framework, which is built into the product.  We understand however that customers may want to choose to use a different commanding implementation such as MVVMLights RelayCommand, or PRISM’s DelegateCommand.  Thankfully extending the current ButtonTool to expose an ICommand property is trivial.

In this post I will show you how you can extend the existing ButtonTool for XamRibbon to allow you to connect and execute ICommand objects from it.

Ribbon Tools

All XamRibbon tools consist of two classes, a Tool class which is a logical representation of the tool and is what you work with in the Ribbon API, and a Control class which is the actual visual representation of the Tool in the Ribbon.  Separating ribbon tools this way allows the control to maintain a single logical instance of a specific tool, while showing multiple visual representations if it, say in a Ribbon group and in the Quick Access Toolbar (QAT).

Extending the ButtonTool

To create my extended ButtonTool, I need to create two derived classes.  The first class is derived from ButtonTool and is shown below:

public class ButtonToolEx : ButtonTool
{        
    public ICommand Command
    {
        get { return (ICommand)GetValue(CommandProperty); }
        set { SetValue(CommandProperty, value); }
    }

    public static readonly DependencyProperty CommandProperty =
        DependencyProperty.Register("Command", 
            typeof(ICommand), 
            typeof(ButtonToolEx), 
            null);

    public object CommandParameter
    {
        get { return (object)GetValue(CommandParameterProperty); }
        set { SetValue(CommandParameterProperty, value); }
    }

    public static readonly DependencyProperty CommandParameterProperty =
        DependencyProperty.Register("CommandParameter", 
            typeof(object), 
            typeof(ButtonToolEx), 
            null);

    protected override RibbonToolBaseControl ResolveToolControl()
    {
        return new ButtonToolControlEx(this);
    }
}

You can see that in my derived class I’ve simply added two Dependency Properties, the first named Command which accepts and ICommand and the second names CommandParameter which accepts an object.

I’ve also override the ResolveToolControl method and am returning a new ButtonToolControlEx object, which is shown in the next listing.

public class ButtonToolControlEx : ButtonToolControl
{
    ButtonToolEx _buttonToolEx;

    public ButtonToolControlEx(RibbonToolBase tool)
    {
        _buttonToolEx = tool as ButtonToolEx;
        this.Tool = tool;
    }

    private void ExecuteCommand()
    {
        object commandParameter = _buttonToolEx.CommandParameter;
        ICommand command = _buttonToolEx.Command;
        if ((command != null) && 
            command.CanExecute(commandParameter))
        {
            command.Execute(commandParameter);
        }
    }

    protected override void RaiseToolClick()
    {
        base.RaiseToolClick();

        this.ExecuteCommand();
    }
}

The main part of the ButtonToolControlEx class is the ExecuteCommand method which is responsible for actually executing the assigned Command.  The ExecuteCommand method is called via the overriden RaiseToolClick method whenever the user clicks the ButtonTool.

To see my extended ButtonTool in action, I created a simple project that includes the MVVMLight toolkit.  I created a View that includes the XamRibbon control and three of my derived ButtonToolEx classes. 

<ig:XamRibbon>
    <ig:XamRibbon.Tabs>
        <ig:XamRibbonTabItem Header="Tab One">
            <ig:XamRibbonGroup Caption="Group One">
                <tools:ButtonToolEx Caption="Button One" 
                    Command="{Binding ButtonOneCommand, 
                              Source={StaticResource vm}}" />
                <tools:ButtonToolEx Caption="Button Two" 
                    Command="{Binding ButtonTwoCommand, 
                              Source={StaticResource vm}}" />
                <tools:ButtonToolEx Caption="Button Three" 
                    Command="{Binding ButtonThreeCommand, 
                              Source={StaticResource vm}}" />
            </ig:XamRibbonGroup>
        </ig:XamRibbonTabItem>
    </ig:XamRibbon.Tabs>
</ig:XamRibbon>

You can see that I’ve bound a property on my ViewModel to each buttons Command property to. 

Note that since the tool is not a control I need to explicitly define the bindings source, which in my app is simply a static resource defined in my page.

Using the extended ButtonTool

In my ViewModel I’ve defined three properties which return a RelayCommand, then instaitnaited new RelayCommands in the ViewModel constructor:

public class HomeViewModel : ViewModelBase
{
    private string _message = String.Empty;

    public HomeViewModel()
    {
        this.Message = "No Buttons Pressed";
        this.ButtonOneCommand = new RelayCommand(() =>
                                    { this.Message = "Button One Pressed"; });
        this.ButtonTwoCommand = new RelayCommand(() => 
                                    { this.Message = "Button Two Pressed"; });
        this.ButtonThreeCommand = new RelayCommand(() => 
                                    { this.Message = "Button Three Pressed"; });
    }

    public RelayCommand ButtonOneCommand { get; set; }
    public RelayCommand ButtonTwoCommand { get; set; }
    public RelayCommand ButtonThreeCommand { get; set; }

    public string Message
    {
        get { return _message; }
        set {
            if (value != _message)
            {
                _message = value;
                RaisePropertyChanged("Message");
            }
        }
    }
}

When my sample app is run, I can click each button which via the RelayCommand executes the action and updates the ViewModels Message property, whose value is reflected by the TextBlock in my application.

Conclusion

As I’ve shown in this blog how simple it is to extend the XamRibbons ButtonTool to allow you to execute ICommands when the user clicks the button.  As always, the full source code for the sample is available here.