Ribbon, ItemsSource & DataTemplates

Sep 2, 2010 at 12:25 PM

Hi all,

I have been playing with the August drop of the Ribbon for a few days now and, while it seems to work well in a 'static' manner (i.e. non data-bound), I have had no end of issues when endevouring to data bind and template the ribbon to my requirements.

So, firstly I set up a Ribbon, a data source (an array of MyData instances) and bind the ItemsSource property of the Ribbon to the array as shown here:

<ribbon:RibbonWindow
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:ribbon="http://schemas.microsoft.com/winfx/2006/xaml/presentation/ribbon"
    xmlns:shell="http://schemas.microsoft.com/winfx/2006/xaml/presentation/shell"
    xmlns:system="clr-namespace:System;assembly=mscorlib"
    xmlns:local="clr-namespace:WPFRibbon"
    x:Class="WPFRibbon.Window1"
    Title="Shell" Height="458" Width="720">
    <ribbon:RibbonWindow.Resources>
        <x:Array x:Key="dataArray" Type="{x:Type local:MyData}">
            <local:MyData Name="Tab1"/>
            <local:MyData Name="Tab2"/>
            <local:MyData Name="Tab3"/>
        </x:Array>
    </ribbon:RibbonWindow.Resources>
    <Grid x:Name="LayoutRoot">
        <ribbon:Ribbon x:Name="ribbon1" ItemsSource="{Binding Source={StaticResource dataArray}}"/>
    </Grid>
</ribbon:RibbonWindow>

This results in a window showing three tabs, each with the header "WPFWindow.MyData" (the fully qualified type name of the MyData instance). So far so good, but this is where the problems start. I now want to template my "MyData" instances into RibbonTabs with the header showing the Name property of the associated MyData instance. Easy huh, just a quick modification to:

<ribbon:RibbonWindow
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:ribbon="http://schemas.microsoft.com/winfx/2006/xaml/presentation/ribbon"
    xmlns:shell="http://schemas.microsoft.com/winfx/2006/xaml/presentation/shell"
    xmlns:system="clr-namespace:System;assembly=mscorlib"
    xmlns:local="clr-namespace:WPFRibbon"
    x:Class="WPFRibbon.Window1"
    Title="Shell" Height="458" Width="720">
    <ribbon:RibbonWindow.Resources>
        <x:Array x:Key="dataArray" Type="{x:Type local:MyData}">
            <local:MyData Name="Tab1"/>
            <local:MyData Name="Tab2"/>
            <local:MyData Name="Tab3"/>
        </x:Array>
    </ribbon:RibbonWindow.Resources>
    <Grid x:Name="LayoutRoot">
        <ribbon:Ribbon x:Name="ribbon1" ItemsSource="{Binding Source={StaticResource dataArray}}">
            <ribbon:Ribbon.ItemTemplate>
                <DataTemplate DataType="{x:Type local:MyData}">
                    <ribbon:RibbonTab Header="{Binding Name}"/>
                </DataTemplate>
            </ribbon:Ribbon.ItemTemplate>
        </ribbon:Ribbon>
    </Grid>
</ribbon:RibbonWindow>
Unfortunately, this does precisely bugger all. The ribbon still shows three tabs with the fully qualified class name as the header ('WPFRibbon.MyData'). I tried all sorts of things here such as removing the DataTemplate's DataType attribute or placing the DataTemplate within a setter of the ItemTemplate property on a style for the Ribbon but nothing seems to change this behavior. In short, the Ribbon seems to ignore the values specified in ItemTemplate.
So my next move was to DataTemplate the MyData class outside of the Ribbon and allow WPF to pick up the template when presenting the item. This resulted in the following Xaml:
<ribbon:RibbonWindow
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:ribbon="http://schemas.microsoft.com/winfx/2006/xaml/presentation/ribbon"
    xmlns:shell="http://schemas.microsoft.com/winfx/2006/xaml/presentation/shell"
    xmlns:system="clr-namespace:System;assembly=mscorlib"
    xmlns:local="clr-namespace:WPFRibbon"
    x:Class="WPFRibbon.Window1"
    Title="Shell" Height="458" Width="720">
    <ribbon:RibbonWindow.Resources>
        <x:Array x:Key="dataArray" Type="{x:Type local:MyData}">
            <local:MyData Name="Tab1"/>
            <local:MyData Name="Tab2"/>
            <local:MyData Name="Tab3"/>
        </x:Array>
        <DataTemplate DataType="{x:Type local:MyData}">
            <ribbon:RibbonTab Header="{Binding Name}"/>
        </DataTemplate>
    </ribbon:RibbonWindow.Resources>
    <Grid x:Name="LayoutRoot">
        <ribbon:Ribbon x:Name="ribbon1" ItemsSource="{Binding Source={StaticResource dataArray}}"/>
    </Grid>
</ribbon:RibbonWindow>
Run this and.... booom! An exception with the following call stack:
>	PresentationFramework.dll!System.Windows.Controls.ItemContainerGenerator.RealizedItemBlock.ContainerAt(int index) + 0x28 bytes	
 	PresentationFramework.dll!System.Windows.Controls.ItemContainerGenerator.ContainerFromIndex(int index) + 0xb2 bytes	
 	RibbonControlsLibrary.dll!Microsoft.Windows.Controls.Ribbon.RibbonTab.RibbonTabHeader.get() + 0x139 bytes	
 	RibbonControlsLibrary.dll!Microsoft.Windows.Controls.Ribbon.RibbonTab.OnNotifyHeaderPropertyChanged(System.Windows.DependencyObject d = {Microsoft.Windows.Controls.Ribbon.RibbonTab Header:Tab1 Items.Count:0}, System.Windows.DependencyPropertyChangedEventArgs e = {System.Windows.DependencyPropertyChangedEventArgs}) + 0x103 bytes	
 	RibbonControlsLibrary.dll!Microsoft.Windows.Controls.Ribbon.RibbonTab.OnHeaderChanged(System.Windows.DependencyObject d = {Microsoft.Windows.Controls.Ribbon.RibbonTab Header:Tab1 Items.Count:0}, System.Windows.DependencyPropertyChangedEventArgs e = {System.Windows.DependencyPropertyChangedEventArgs}) + 0x12b bytes	
 	[Native to Managed Transition]	
 	[Managed to Native Transition]	
 	WindowsBase.dll!System.Windows.DependencyObject.OnPropertyChanged(System.Windows.DependencyPropertyChangedEventArgs e) + 0x103 bytes	
 	PresentationFramework.dll!System.Windows.FrameworkElement.OnPropertyChanged(System.Windows.DependencyPropertyChangedEventArgs e = {System.Windows.DependencyPropertyChangedEventArgs}) + 0x254 bytes	
 	WindowsBase.dll!System.Windows.DependencyObject.NotifyPropertyChange(System.Windows.DependencyPropertyChangedEventArgs args = {System.Windows.DependencyPropertyChangedEventArgs}) + 0xbb bytes	
 	WindowsBase.dll!System.Windows.DependencyObject.UpdateEffectiveValue(System.Windows.EntryIndex entryIndex, System.Windows.DependencyProperty dp, System.Windows.PropertyMetadata metadata, System.Windows.EffectiveValueEntry oldEntry = {System.Windows.EffectiveValueEntry}, ref System.Windows.EffectiveValueEntry newEntry, bool coerceWithDeferredReference, System.Windows.OperationType operationType) + 0xb5e bytes	
 	PresentationFramework.dll!System.Windows.StyleHelper.ApplyTemplatedParentValue(System.Windows.DependencyObject container, MS.Internal.FrameworkObject child, int childIndex, ref MS.Utility.FrugalStructList childRecordFromChildIndex, System.Windows.DependencyProperty dp, System.Windows.FrameworkElementFactory templateRoot) + 0x1a8 bytes	
 	PresentationFramework.dll!System.Windows.StyleHelper.InvalidatePropertiesOnTemplateNode(System.Windows.DependencyObject container, MS.Internal.FrameworkObject child = {Microsoft.Windows.Controls.Ribbon.RibbonTab Header:Tab1 Items.Count:0}, int childIndex, ref MS.Utility.FrugalStructList childRecordFromChildIndex, bool isDetach, System.Windows.FrameworkElementFactory templateRoot) + 0x138 bytes	
 	PresentationFramework.dll!System.Windows.StyleHelper.LoadOptimizedTemplateContent(System.Windows.DependencyObject container, System.Windows.Markup.ParserContext parserContext = {System.Windows.Markup.ParserContext}, System.Windows.Markup.OptimizedTemplateContent optimizedTemplateContent, System.Windows.FrameworkTemplate frameworkTemplate, System.Windows.Markup.IComponentConnector componentConnector, System.Windows.Markup.IStyleConnector styleConnector, System.Collections.Generic.List affectedChildren, System.Windows.UncommonField templatedNonFeChildrenField) + 0x606 bytes	
 	PresentationFramework.dll!System.Windows.FrameworkTemplate.LoadContent(System.Windows.DependencyObject container, System.Collections.Generic.List affectedChildren, System.Windows.UncommonField templatedNonFeChildrenField) + 0xc1 bytes	
 	PresentationFramework.dll!System.Windows.StyleHelper.ApplyTemplateContent(System.Windows.UncommonField dataField, System.Windows.DependencyObject container, System.Windows.FrameworkElementFactory templateRoot, int lastChildIndex, System.Collections.Specialized.HybridDictionary childIndexFromChildID, System.Windows.FrameworkTemplate frameworkTemplate) + 0x3fb bytes	
 	PresentationFramework.dll!System.Windows.FrameworkTemplate.ApplyTemplateContent(System.Windows.UncommonField templateDataField, System.Windows.FrameworkElement container) + 0x64 bytes	
 	PresentationFramework.dll!System.Windows.FrameworkElement.ApplyTemplate() + 0x6a bytes	
 	PresentationFramework.dll!System.Windows.FrameworkElement.MeasureCore(System.Windows.Size availableSize) + 0xf9 bytes	
 	PresentationCore.dll!System.Windows.UIElement.Measure(System.Windows.Size availableSize = {INF;INF}) + 0x3be bytes	
 	PresentationFramework.dll!System.Windows.Controls.Border.MeasureOverride(System.Windows.Size constraint) + 0x30e bytes	
 	PresentationFramework.dll!System.Windows.FrameworkElement.MeasureCore(System.Windows.Size availableSize) + 0x40f bytes	
 	PresentationCore.dll!System.Windows.UIElement.Measure(System.Windows.Size availableSize = {INF;INF}) + 0x3be bytes	
 	PresentationFramework.dll!System.Windows.Controls.Border.MeasureOverride(System.Windows.Size constraint) + 0x30e bytes	
 	PresentationFramework.dll!System.Windows.FrameworkElement.MeasureCore(System.Windows.Size availableSize) + 0x40f bytes	
 	PresentationCore.dll!System.Windows.UIElement.Measure(System.Windows.Size availableSize = {INF;INF}) + 0x3be bytes	
 	PresentationFramework.dll!System.Windows.Controls.Grid.MeasureOverride(System.Windows.Size constraint = {INF;INF}) + 0x14c bytes	
 	PresentationFramework.dll!System.Windows.FrameworkElement.MeasureCore(System.Windows.Size availableSize) + 0x40f bytes	
 	PresentationCore.dll!System.Windows.UIElement.Measure(System.Windows.Size availableSize = {INF;INF}) + 0x3be bytes	
 	PresentationFramework.dll!System.Windows.Controls.Control.MeasureOverride(System.Windows.Size constraint) + 0x9e bytes	
 	PresentationFramework.dll!System.Windows.FrameworkElement.MeasureCore(System.Windows.Size availableSize) + 0x40f bytes	
 	PresentationCore.dll!System.Windows.UIElement.Measure(System.Windows.Size availableSize = {INF;INF}) + 0x3be bytes	
 	RibbonControlsLibrary.dll!Microsoft.Windows.Controls.Ribbon.Primitives.RibbonTabHeadersPanel.InitialMeasure(System.Windows.Size constraint = {INF;INF}, out double totalDefaultPaddingAllTabHeaders = 18.0, out double totalDefaultPaddingRegularTabHeaders = 0.0, out double totalDesiredWidthRegularTabHeaders = 0.0, out int countRegularTabs = 0, out int countVisibleTabs = 0) + 0x3fa bytes	
 	RibbonControlsLibrary.dll!Microsoft.Windows.Controls.Ribbon.Primitives.RibbonTabHeadersPanel.MeasureOverride(System.Windows.Size availableSize = {646;INF}) + 0x2dd bytes	
 	PresentationFramework.dll!System.Windows.FrameworkElement.MeasureCore(System.Windows.Size availableSize) + 0x40f bytes	
 	PresentationCore.dll!System.Windows.UIElement.Measure(System.Windows.Size availableSize = {646;INF}) + 0x3be bytes	
 	PresentationCore.dll!System.Windows.ContextLayoutManager.UpdateLayout() + 0x3ec bytes	
 	PresentationCore.dll!System.Windows.ContextLayoutManager.UpdateLayoutCallback(object arg) + 0x2d bytes	
 	PresentationCore.dll!System.Windows.Media.MediaContext.FireInvokeOnRenderCallbacks() + 0xd0 bytes	
 	PresentationCore.dll!System.Windows.Media.MediaContext.RenderMessageHandlerCore(object resizedCompositionTarget) + 0xd2 bytes	
 	PresentationCore.dll!System.Windows.Media.MediaContext.RenderMessageHandler(object resizedCompositionTarget) + 0x95 bytes	
 	WindowsBase.dll!System.Windows.Threading.ExceptionWrapper.InternalRealCall(System.Delegate callback, object args, bool isSingleParameter) + 0x11a bytes	
 	WindowsBase.dll!System.Windows.Threading.ExceptionWrapper.TryCatchWhen(object source = {System.Windows.Threading.Dispatcher}, System.Delegate callback, object args, bool isSingleParameter, System.Delegate catchHandler = null) + 0x4c bytes	
 	WindowsBase.dll!System.Windows.Threading.DispatcherOperation.InvokeImpl() + 0x9a bytes	
 	mscorlib.dll!System.Threading.ExecutionContext.runTryCode(object userData) + 0x17c bytes	
 	[Native to Managed Transition]	
 	[Managed to Native Transition]	
 	mscorlib.dll!System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext executionContext, System.Threading.ContextCallback callback, object state) + 0x8f bytes	
 	WindowsBase.dll!System.Windows.Threading.DispatcherOperation.Invoke() + 0x5e bytes	
 	WindowsBase.dll!System.Windows.Threading.Dispatcher.ProcessQueue() + 0x29b bytes	
 	WindowsBase.dll!System.Windows.Threading.Dispatcher.WndProcHook(System.IntPtr hwnd, int msg, System.IntPtr wParam, System.IntPtr lParam, ref bool handled) + 0x9f bytes	
 	WindowsBase.dll!MS.Win32.HwndWrapper.WndProc(System.IntPtr hwnd, int msg, System.IntPtr wParam, System.IntPtr lParam, ref bool handled) + 0x101 bytes	
 	WindowsBase.dll!MS.Win32.HwndSubclass.DispatcherCallbackOperation(object o) + 0x9b bytes	
 	WindowsBase.dll!System.Windows.Threading.ExceptionWrapper.InternalRealCall(System.Delegate callback, object args, bool isSingleParameter) + 0x11a bytes	
 	WindowsBase.dll!System.Windows.Threading.ExceptionWrapper.TryCatchWhen(object source = {System.Windows.Threading.Dispatcher}, System.Delegate callback, object args, bool isSingleParameter, System.Delegate catchHandler = null) + 0x4c bytes	
 	WindowsBase.dll!System.Windows.Threading.Dispatcher.InvokeImpl(System.Windows.Threading.DispatcherPriority priority, System.TimeSpan timeout, System.Delegate method, object args, bool isSingleParameter) + 0xd9 bytes	
 	WindowsBase.dll!MS.Win32.HwndSubclass.SubclassWndProc(System.IntPtr hwnd, int msg, System.IntPtr wParam, System.IntPtr lParam) + 0x225 bytes	
 	[Native to Managed Transition]	
 	[Managed to Native Transition]	
 	WindowsBase.dll!System.Windows.Threading.Dispatcher.TranslateAndDispatchMessage(ref System.Windows.Interop.MSG msg) + 0x49 bytes	
 	WindowsBase.dll!System.Windows.Threading.Dispatcher.PushFrameImpl(System.Windows.Threading.DispatcherFrame frame) + 0xe9 bytes	
 	PresentationFramework.dll!System.Windows.Application.RunInternal(System.Windows.Window window) + 0x174 bytes	
 	WPFRibbon.exe!WPFRibbon.App.Main() + 0x70 bytes	C#
 	[Native to Managed Transition]	
 	[Managed to Native Transition]	
 	Microsoft.VisualStudio.HostingProcess.Utilities.dll!Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly() + 0x47 bytes	
 	mscorlib.dll!System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext executionContext, System.Threading.ContextCallback callback, object state) + 0x7f bytes	
 	mscorlib.dll!System.Threading.ThreadHelper.ThreadStart() + 0x50 bytes	
Excellent. Nothing to do with me there, so now I'm a bit lost. I did venture a bit further and change the RibbonTab header property to a static value ('Text') rather than a binding (which gave me the same error) and finally removed the Header property so the Xaml looked as follows:
<ribbon:RibbonWindow
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:ribbon="http://schemas.microsoft.com/winfx/2006/xaml/presentation/ribbon"
    xmlns:shell="http://schemas.microsoft.com/winfx/2006/xaml/presentation/shell"
    xmlns:system="clr-namespace:System;assembly=mscorlib"
    xmlns:local="clr-namespace:WPFRibbon"
    x:Class="WPFRibbon.Window1"
    Title="Shell" Height="458" Width="720">
    <ribbon:RibbonWindow.Resources>
        <x:Array x:Key="dataArray" Type="{x:Type local:MyData}">
            <local:MyData Name="Tab1"/>
            <local:MyData Name="Tab2"/>
            <local:MyData Name="Tab3"/>
        </x:Array>
        <DataTemplate DataType="{x:Type local:MyData}">
            <ribbon:RibbonTab />
        </DataTemplate>
    </ribbon:RibbonWindow.Resources>
    <Grid x:Name="LayoutRoot">
        <ribbon:Ribbon x:Name="ribbon1" ItemsSource="{Binding Source={StaticResource dataArray}}"/>
    </Grid>
</ribbon:RibbonWindow>
At this point the application runs again and I get three tabs but, predictably, the tab headers are empty (at least showing that the template is being applied).
I've now given up but would be very intrigued if anyone knows of why any of the above might be happening or whether there is a workaround to get this running as expected.
Thanks in advance,
  Ian
Sep 7, 2010 at 6:44 AM

Hi Ian,

you need to use Styles instead of DataTemplates:

<Style TargetType="{x:Type ribbon:RibbonTab}">
    <Setter Property="Header" Value="{Binding Header}" />
</Style>

For a more detailed example download Microsoft Ribbon for WPF Source and Samples.msi at http://www.microsoft.com/downloads/details.aspx?FamilyID=2bfc3187-74aa-4154-a670-76ef8bc2a0b4&displaylang=en

I think in the file UserControlMVVM.xaml is all you need.

Sep 10, 2010 at 2:53 PM

Why don't datatemplates work?

Sep 21, 2010 at 9:03 AM

Hi

I tried to bind  the RibbonTabs and Groups to my own business objects. Also I tried to set all properties through styles. Look at the following code.

The binding works fine but on the display the RibbonButtons are not arranged as they should some how the group header is being moved from its place. can you please take a look at it?

        <DataTemplate x:Key="RibbonTabTemplate">
            <ribbon:RibbonTab />
        </DataTemplate>
        <Style TargetType="{x:Type ribbon:RibbonTab}">
            <Setter Property="Header" Value="{Binding Path=Title}" />
            <Setter Property="ItemsSource" Value="{Binding Path=Groups}"/>
            <Setter Property="ItemTemplate">
                <Setter.Value>
                    <DataTemplate>
                        <ribbon:RibbonGroup   Header="{Binding Path=Title}" />
                    </DataTemplate>
                </Setter.Value>
            </Setter>
        </Style>
        <Style TargetType="{x:Type ribbon:RibbonGroup}">
            <Setter Property="ItemsSource"  Value="{Binding Path=Actions}"/>
            
            <Setter Property="ItemTemplate">
                <Setter.Value>
                    <DataTemplate>
                        <ribbon:RibbonButton/>
                    </DataTemplate>
                </Setter.Value>
            </Setter>
        </Style>
        <Style TargetType="{x:Type ribbon:RibbonButton}">
            <Setter Property="Label" Value="{Binding Path=Title}"/>
            <Setter Property="SmallImageSource" Value="/Banan.Bcc.UI.Wpf.Presentation;component/Resources/Images/SmallIcon.png"/>
        </Style>