One common request that we are receiving recently is how to integrate the RadChart and RadContextMenu controls so they play nicely together.

A typical real-life scenario would probably require that each ChartItem should display different (context-specific) set of menu items. In order to address the increased interest in this functionality we decided to add a new integration example for the Q1 2010 release but for those of you that need something working right now, here is a quick overview of the required steps to add context-specific menu to Bar series items in RadChart (this can be applied to any series type).

Basically, the approach that I will be demonstrating here combines the MVVM support of RadChart with RadChart data-binding, and the data-binding functionality supported by the RadMenu (RadContextMenu) control:

  • First, in order to use RadContextMenu you have to set your application to be windowless for Silverlight (to disable the right click that displays information about Silverlight).
  • Now you need to re-template the desired chart series type in order to place a RadContextMenu instance as attached property in its template:

Silverlight code:

<Grid x:Name="LayoutRoot">
   
<Grid.Resources>
        ...


        <
Style x:Key="CustomStyle" TargetType="chart:Bar">
            <Setter Property
="Template" >
                <Setter
.Value>
                    <ControlTemplate TargetType
="chart:Bar">
                        <Canvas Opacity
="0" x:Name="PART_CanvasContainer">

                            <navigation
:RadContextMenu.ContextMenu>
                                <navigation
:RadContextMenu ItemsSource="{Binding DataItem.MenuItems}"
                                                           ItemTemplate
="{StaticResource MenuItemTemplate}
"
                                                           ItemClick
="OnContextMenuClick" />

                            <
/navigation:RadContextMenu.ContextMenu>

                            <Rectangle x
:Name="PART_DefiningGeometry" 
                               Height
="{TemplateBinding ItemActualHeight}"
                               Width
="{TemplateBinding ItemActualWidth}"
                               Style
="{TemplateBinding ItemStyle}"
                               RadiusX
="{StaticResource BarRadiusX}"
                               RadiusY
="{StaticResource BarRadiusY}" />
                            <Rectangle Height
="{TemplateBinding ItemActualHeight}"
                               Width
="{TemplateBinding ItemActualWidth}"
                               RadiusX
="{StaticResource BarRadiusX}"
                               RadiusY
="{StaticResource BarRadiusY}"
                               OpacityMask
="{StaticResource BarOpacityMaskBrush}"
                               Fill
="{StaticResource BarMaskBrush}" />
                            <Rectangle Height
="{TemplateBinding ItemActualHeight}"
                               Width
="{TemplateBinding ItemActualWidth}"
                               RadiusX
="{StaticResource BarRadiusX}"
                               RadiusY
="{StaticResource BarRadiusY}"
                               Fill
="{StaticResource BarTopMaskBrush}" />
                            <Canvas
.RenderTransform>
                                <ScaleTransform x
:Name="PART_AnimationTransform" ScaleY="0" />
                            <
/Canvas.RenderTransform>
                        <
/Canvas>
                    <
/ControlTemplate>
                <
/Setter.Value>
            <
/Setter>
        </
Style>
   
</Grid.Resources>
    ...
</Grid>
 
WPF code:
<Grid x:Name="LayoutRoot">
   
<Grid.Resources>
       
<HierarchicalDataTemplate x:Key="MenuItemTemplate" ItemsSource="{Binding Items}">
           
<TextBlock Text="{Binding Text}" />
        </
HierarchicalDataTemplate>

       
<Style x:Key="CustomStyle" TargetType="telerikCharting:Bar">
            <Setter Property
="Template" >
                <Setter
.Value>
                    <ControlTemplate TargetType
="telerikCharting:Bar">
                        <Canvas Opacity
="0" x:Name="PART_CanvasContainer">

                            <telerikNavigation
:RadContextMenu.ContextMenu>
                                <telerikNavigation
:RadContextMenu ItemsSource="{Binding DataItem.MenuItems}"
                                                           ItemTemplate
="{StaticResource MenuItemTemplate}
"
                                                           ItemClick
="OnContextMenuClick">

                                    <telerikNavigation
:RadContextMenu.ItemContainerStyle>
                                        <Style TargetType
="telerikNavigation:RadMenuItem">
                                            <Setter Property
="IsCheckable" Value="{Binding IsCheckable}" />
                                            <Setter Property
="IsChecked" Value="{Binding IsChecked}" />
                                            <Setter Property
="IsSeparator" Value="{Binding IsSeparator}" />
                                            <Setter Property
="IsEnabled" Value="{Binding IsEnabled}" />
                                            <Setter Property
="StaysOpenOnClick" Value="{Binding StaysOpenOnClick}" />
                                            <Setter Property
="Icon" Value="{Binding Image}" />
                                        </
Style>

                                   
</telerikNavigation:RadContextMenu.ItemContainerStyle>
                               
</telerikNavigation:RadContextMenu>
                           
</telerikNavigation:RadContextMenu.ContextMenu>

                           
<Rectangle x:Name="PART_DefiningGeometry" 
                               Height
="{TemplateBinding ItemActualHeight}"
                               Width
="{TemplateBinding ItemActualWidth}"
                               Style
="{TemplateBinding ItemStyle}"
                               RadiusX
="{StaticResource BarRadiusX}"
                               RadiusY
="{StaticResource BarRadiusY}" />
                            <
Rectangle Height="{TemplateBinding ItemActualHeight}"
                               Width
="{TemplateBinding ItemActualWidth}"
                               RadiusX
="{StaticResource BarRadiusX}"
                               RadiusY
="{StaticResource BarRadiusY}"
                               OpacityMask
="{StaticResource BarOpacityMaskBrush}"
                               Fill
="{StaticResource BarMaskBrush}" />
                            <
Rectangle Height="{TemplateBinding ItemActualHeight}"
                               Width
="{TemplateBinding ItemActualWidth}"
                               RadiusX
="{StaticResource BarRadiusX}"
                               RadiusY
="{StaticResource BarRadiusY}"
                               Fill
="{StaticResource BarTopMaskBrush}" />
                            <
Canvas.RenderTransform>
                               
<ScaleTransform x:Name="PART_AnimationTransform" ScaleY="0" />
                            </
Canvas.RenderTransform>
                       
</Canvas>
                   
</ControlTemplate>
               
</Setter.Value>
           
</Setter>
       
</Style>
   
</Grid.Resources>
    ...
</Grid>
  • Notice that the ContextMenu is databound to a property of your chart DataItem instance that exposes the desired menu items’ structure (i.e. the chart control is databound to a List of ChartDataItem objects, and the ChartDataItem class exposes MenuItems property; {Binding DataItem.MenuItems} holds reference to a specific ChartDataItem instance).
  • Notice that the RadContextMenu can support hierarchical Menu structure as well (as with stand-alone Menu you need to create a HierarchicalDataTemplate that specifies the ItemsSource, ItemTemplate and other properties of the item's children).
  • Also, notice the difference between WPF and Silverlight when binding RadMenuItem properties, such as IsSeparator, Icon, IsEnabled, etc.
    • For WPF you can use the ItemsControl.ItemContainerStyle to achieve the desired functionality.
    • For Silverlight you can use the ContainerBindings mechanism implemented by Telerik that allows you to bind properties of an item container (RadMenuItem, RadTreeViewItem, etc.) to the properties of the data objects. The container bindings are always set through a DataTemplate or HierarchicalDataTemplate:

Silverlight code:

<Grid x:Name="LayoutRoot">
   
<Grid.Resources>
       
<telerik:ContainerBindingCollection x:Key="ContainerBindings">
           
<telerik:ContainerBinding PropertyName="IsCheckable" Binding="{Binding IsCheckable}" />
            <
telerik:ContainerBinding PropertyName="IsChecked" Binding="{Binding IsChecked}" />
            <
telerik:ContainerBinding PropertyName="IsSeparator" Binding="{Binding IsSeparator}" />
            <
telerik:ContainerBinding PropertyName="IsEnabled" Binding="{Binding IsEnabled}" />
            <
telerik:ContainerBinding PropertyName="StaysOpenOnClick" Binding="{Binding StaysOpenOnClick}" />
            <
telerik:ContainerBinding PropertyName="Icon" Binding="{Binding Image}" />
        </
telerik:ContainerBindingCollection>

       
<telerik:HierarchicalDataTemplate x:Key="MenuItemTemplate" ItemsSource="{Binding Items}"
            telerik:ContainerBinding.ContainerBindings
="{StaticResource ContainerBindings}">
           
<TextBlock Text="{Binding Text}" />
        </
telerik:HierarchicalDataTemplate>

   
</Grid.Resources>
    ...
</Grid>
  • I will not list the actual business classes required for the data binding of the chart and the menu controls (MenuItem, MenuItemCollection, ChartDataItem, ChartDataItemCollection) as there is nothing special about them – you can find them in the runnable sample application attached to this post. What is left is to add the chart control declaration, bind it to a property of the ViewModel set as a DataContext for the respective user control, and instruct the control to use the custom bar style declared earlier:
<control:RadChart x:Name="RadChart1" ItemsSource="{Binding Data}">
   
<control:RadChart.SeriesMappings>
        
       
<chart:SeriesMapping>    
           
<chart:SeriesMapping.SeriesDefinition>
               
<chart:BarSeriesDefinition ItemStyle="{StaticResource CustomStyle}" />
            </
chart:SeriesMapping.SeriesDefinition>
           
<chart:SeriesMapping.ItemMappings>
               
<chart:ItemMapping FieldName="YValue" DataPointMember="YValue" />
            </
chart:SeriesMapping.ItemMappings>
       
</chart:SeriesMapping>

   
</control:RadChart.SeriesMappings>
</control:RadChart>
 
Here is the final result:
chart_contextmenu_integration 
 
 
Hope this helps.

About the Author

Vesselin Georgiev

 is Team Lead in Telerik XAML DataViz Team

Related Posts

Comments

Comments are disabled in preview mode.