Extending a XamRibbon ButtonTool to execute ICommands

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.


Comments  (4 )

Anze
on Mon, Sep 19 2011 7:13 AM

How about controlling the button tool's enabled state from within ViewModel using ICommand's CanExecute method / CanExecuteChanged event?

BTW How come Command property was not added to the ButtonTool? I'm sure a lot of developers would agree that it is essential for WPF/Silverlight applications (even standard Silverlight Button control has it!). Are there any plans to add it in the future releases?

gxclarke
on Tue, Oct 4 2011 10:26 AM

I have tried your solution and noticed that ResolveToolControl  method is called twice in the version 11.1.20111.2097.  Is it Ok?

akichkailo
on Wed, Oct 5 2011 10:26 AM

It is better to override OnToolClick() method in the ButtonToolEx class instead of overriding RaiseToolClick()  method in the ButtonToolControlEx.

The ButtonToolControlEx class is only needed to initialize Tool property because its setter is internal :(

Please see this post for more information:

blogs.infragistics.com/.../307802.aspx

Andre
on Tue, Jul 23 2013 6:20 PM

Thanks for this solution. I've attempted to use your code to extend my verions of XamRibbon 2010.2 and I see that RibbonToolBaseControl, RibbonToolBase and ButtonToolControl all cannot be resolved. Is your solution SL specific (my project is WPF) or is this the difference in the APIs with older version of NA library? Any assistance would be appreciated.

Add a Comment

Please Login or Register to add a comment.