Data Binding the IsVisible Property of ContextualTabGroup

XamRibbon allows you to have groups of tabs that only display when the application is in a certain state or condition.  In other words, it offers context-sensitive tab groups.  You can use them by adding ContextualTabGroup objects to the ContextualTabGroups property of XamRibbon.  If you want to hide a ContextualTabGroup, you simply set its IsVisible property to false.

What if you want to data bind IsVisible to a property on a ViewModel object?  Then the situation becomes a little bit more difficult, because ContextualTabGroup is not a visual element and does not exist in the element tree.  A brief glance in Mole can prove that this is the case:


Notice how the Visual Tree, on the left, does not contain a ContextualTabGroup instance, but the Visual Tree elements have their DataContext set to an instance of ContextualTabGroup.  It turns out that ContextualTabGroup is more like a container for settings, not a full-fledged UI element.  One important ramification of this seemingly minor detail is that ContextualTabGroup objects are not in the logical tree, which means they do not have an inheritance context, which means that their properties cannot be data bound like properties of a normal UI element in the element tree.

I recently wrote of a way to work around the fact that some objects declared in XAML do not have an inheritance context, in my “Binding a XamDataGrid Field Property” post.  In this post, I will show another, far more elegant, way to deal with this problem.  I am basing my work here on the “Model-see, Model-do, and the Poo is Optional” post by Mike Hillberg, of Microsoft.

My objective is simple.  I want to have a CheckBox bound to a property on a simple ViewModel class, and the IsVisible property of a ContextualTabGroup bound to that same property.  When the CheckBox is checked, the tab group displays.


When the CheckBox is unchecked, the tab group goes away.

The ViewModel class, an instance of which serves as the Window’s DataContext, looks like this:

class MyViewModel: INotifyPropertyChanged
{
    bool _showContextualTabGroup;
    public bool ShowContextualTabGroup
    {
        get { return _showContextualTabGroup; }
        set
        {
            if (value == _showContextualTabGroup)
                return;

            _showContextualTabGroup = value;

            this.OnPropertyChanged("ShowContextualTabGroup");
        }
    }

    #region INotifyPropertyChanged Members

    public event PropertyChangedEventHandler PropertyChanged;

    void OnPropertyChanged(string propertyName)
    {
        if (this.PropertyChanged != null)
            this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
    }

    #endregion
}

The trick to making this work is in my DataContextSpy class.  It provides an artificial inheritance context with which the ContextualTabGroup can gain access to the Window’s ViewModel via one simple data binding expression.   Naturally, you can use DataContextSpy for much more than just this one particular task.  Here is the class definition:

public class DataContextSpy
    : Freezable // Enable ElementName and DataContext bindings
{
    public DataContextSpy()
    {
        // This binding allows the spy to inherit a DataContext.
        BindingOperations.SetBinding(this, DataContextProperty, new Binding());
    }

    public object DataContext
    {
        get { return (object)GetValue(DataContextProperty); }
        set { SetValue(DataContextProperty, value); }
    }

    // Borrow the DataContext dependency property from FrameworkElement.
    public static readonly DependencyProperty DataContextProperty =
        FrameworkElement.DataContextProperty.AddOwner(typeof(DataContextSpy));
           
    protected override Freezable CreateInstanceCore()
    {
        // We are required to override this abstract method.
        throw new NotImplementedException();
    }
}

Now here is the XAML content of the Window, which contains the XamRibbon and DataContextSpy:

<DockPanel>
  <CheckBox
    DockPanel.Dock="Bottom"
    Content="Show ContextualTabGroup"
    IsChecked="{Binding Path=ShowContextualTabGroup}"
    Margin="10"
    />

  <igRibbon:XamRibbon DockPanel.Dock="Top">
    <igRibbon:XamRibbon.Resources>
      <local:DataContextSpy x:Key="spy" />
    </igRibbon:XamRibbon.Resources>

    <igRibbon:XamRibbon.ContextualTabGroups>
      <igRibbon:ContextualTabGroup
        Caption="I am a ContextualTabGroup"
        IsVisible="{Binding
          Source={StaticResource spy},
          Path=DataContext.ShowContextualTabGroup}"

        >
        <igRibbon:RibbonTabItem
          Header="I am a RibbonTabItem in the ContextualTabGroup"
          />
      </igRibbon:ContextualTabGroup>
    </igRibbon:XamRibbon.ContextualTabGroups>
  </igRibbon:XamRibbon>   
 
</DockPanel>

You can download the source code here. Note: this project requires Visual Studio 2008 and NetAdvantage for WPF v7.2 or later to compile.


Add a Comment

Please Login or Register to add a comment.