Using XamRibbon ButtonTool in MVVM applications

[Infragistics] Ivo Evtimov / Tuesday, July 26, 2011

The Problem

Using XamRibbon’s ButtonTool control in a MVVM application may present some challenges when attempting to wire-up binding against the control. The problem arises since the ButtonTool control inherits from the RibbonTool class which is not a framework element. A non-framework element class does not allow the ViewModel (set as the DataContext of the View) to be referenced in the binding of the Click event of the ButtonTool control. Customers using frameworks like Caliburn and MVVM Light often encounter this problem.

The Solution

There are a number of different approaches available to reliably setup binding against the control. Some solutions include extending the control to support commanding. The method presented below is a little different. As non-framework elements can reference static resources the following solution “captures” the instance of the ViewModel in a Resource and references this resource in the Click event.

To begin, define the class that holds the instance of the ViewModel:

public class MyViewModelHolder : INotifyPropertyChanged
    {
        private object _instance;
        public object Instance
        {
            get
            {
                return _instance;
            }
            set
            {
                _instance = value;
                NotifyPropertyChanged("Instance");
            }
        }        
        public event PropertyChangedEventHandler PropertyChanged;
       
        public void NotifyPropertyChanged(string propertyName)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }
    }

Note that the Holder class implements INotifyPropertyChanged – this is required because the binding that is implemented next must be notified when the Instance property is changed. The Instance property is of type Object so this class can be used to hold a reference to any of the ViewModel classes in the solution.

Next, define the resource:

<UserControl.Resources>
    <!--create an empty class that will hold the original DataContext(VM) class-->
    <local:MyViewModelHolder x:Key="VM_Holder"/>       
</UserControl.Resources>
<!--on Loaded populate the Instance Property with the current DataContext-->
<i:Interaction.Triggers>
    <i:EventTrigger EventName="Loaded">
        <ei:ChangePropertyAction TargetObject="{StaticResource VM_Holder}" PropertyName="Instance" Value="{Binding}"/>
    </i:EventTrigger>
</i:Interaction.Triggers>

Required namespaces are:

xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
xmlns:ei="http://schemas.microsoft.com/expression/2010/interactions"

and local is the namespace of our Holder class.

Next, is the XamRibbon’s ButtonTool implementation:

<igRibbon:XamRibbon x:Name="ribMyRibbon" Caption="Ribbon">           
    <igRibbon:XamRibbon.Tabs>
        <igRibbon:XamRibbonTabItem>                                                        
            <igRibbon:XamRibbonGroup>
                <igRibbon:ButtonTool IsQatCommonTool="True" 
                                      Caption="Click me">
                    <i:Interaction.Triggers>
                        <i:EventTrigger  EventName="Click" >
                            <!--Use the Resourse instance as TargetObject because the DataContext in unavailable for RibbonTool ansestors-->
                            <ei:CallMethodAction
                                TargetObject="{Binding Source={StaticResource VM_Holder}, Path=Instance}"
                                MethodName="SayHello1" /> 
                        </i:EventTrigger>
                    </i:Interaction.Triggers> 
                </igRibbon:ButtonTool>                       
            </igRibbon:XamRibbonGroup>
        </igRibbon:XamRibbonTabItem>
    </igRibbon:XamRibbon.Tabs>
</igRibbon:XamRibbon>

Conclusion

This approach is flexible for use in many situations as it’s not dependent on any MVVM frameworks. This approach is valid in all cases when binding non-framework elements is required.