Jump to content
Software FX Community

customize legend items


zac morris

Recommended Posts

 Hi,

I would like to be able to customize the legend items on a multi-series line chart.

By default each legend item is labeled with the value of the 'BindingPath' for the series.  For example, if 4 different series are each bound to the 'myYvalue' property of items in their respective SeriesAttributes.ItemsSource collection, then the legend will display 4 items each labeled 'myYvalue'.

How do I build a template for legend items so that the 'name' (as defined by me) of each series is displayed?

I've tried reproducing  the following code (and datatemplate) from the RichContentPage from the AppDemo sample solution but it doesn't work:

  chart1.LegendBox.ItemAttributes[chart1.Series].Visibility = Visibility.Collapsed;   chart1.LegendBox.ItemAttributes[chart1.AxisX].Template = (DataTemplate) FindResource("RichContentLegend");

 

Thanks,

Zac Morris 

Link to comment
Share on other sites

>> How do I build a template for legend items so that the 'name' (as defined by me) of each series is displayed?

You have 3 options depending on the effect you want to achieve

1) If you just want to replace the default string with another string

Use the SeriesAttributes.Content property, e.g.

SeriesAttributes series = new SeriesAttributes();series.BindingPath = "myYvalue";series.Content = "Series Title";series.ItemsSource = enumerable;

2) If you want to keep the marker but want to display something other than a string

Use the SeriesAttributes Content and ContentTemplate properties. Although Content is declared as object you should not place a visual in it because we use content in several places (LegendBox, ToolTips, DataView) and a visual can only be hosted in one parent. Instead you can assign each of your SeriesAttributes objects a CLR object in the content property and then write a template that shows some of the CLR properties, e.g. if your CLR object has ProductName and Version properties you could use a template with a couple of TextBlocks bound to these CLR properties.

3) If you want to completely control how the legend item looks

Use the LegendItemAttributes.Template property, the code you tried was very close, in a multi-series chart we will show the series attributes in the legend box so your code should be

chart1.LegendBox.ItemAttributes[chart1.Series].Template = (DataTemplate) FindResource("RichContentLegend");

Note that the template will be bound to a logical object that has several properties, e.g. Fill, Stroke, etc. These are the series properties so that you can allow the user a way to identify the series. It also exposes Content and ContentTemplate properties which will have the values set in option 2.

Regards,

JuanC

 

Link to comment
Share on other sites

  • 2 months later...

Hello, I'm trying to achieve #2 above but I'm not clear how I should use Content and ContentTemplate properties of SeriesAttributes. I either get a Legend with the marker (pie slice) and no label, or a label with no marker. Please help.

<cfx:Chart Name="chart1" ItemsSource="{Binding Source={StaticResource SpendByChannelProvider}}" Gallery="Pie" HorizontalAlignment="Left" Width="388.103" Grid.ColumnSpan="2" Margin="0,0,0,13" Grid.RowSpan="3">

<cfx:SeriesAttributes BindingPath="spend" Template="{StaticResource Pie2}">

<cfx:SeriesAttributes.PointLabels>

<cfx:PointLabelAttributes Visibility="Visible">

<cfx:PointLabelAttributes.Template>

<DataTemplate>

<Label Content="{Binding Path=DataItem.Channel}" Background="Transparent" Foreground="Black" FontFamily="Calbiri" FontSize="12"/>

</DataTemplate>

</cfx:PointLabelAttributes.Template>

</cfx:PointLabelAttributes>

</cfx:SeriesAttributes.PointLabels>

 

 

<cfx:SeriesAttributes.ContentTemplate>

<DataTemplate>

<TextBlock x:Name="BarText" Text="{Binding Path=DataItem.Channel}" HorizontalAlignment="Left" VerticalAlignment="Center"/>

</DataTemplate>

</cfx:SeriesAttributes.ContentTemplate>

<cfx:SeriesAttributes.Content>

<TextBlock x:Name="BarText" Text="{Binding Path=DataItem.Channel}" HorizontalAlignment="Left" VerticalAlignment="Center"/>

</cfx:SeriesAttributes.Content>

</cfx:SeriesAttributes>

 

<cfx:Chart.LegendBox>

<cfx:LegendBox>

</cfx:LegendBox></cfx:Chart.LegendBox>

 

</cfx:Chart>
Link to comment
Share on other sites

To achieve option #2 (displaying something more complex than a single string on the legendbox) you have to do one of the following

1) Specify the ContentTemplate in XAML but specifying the Content in code

Assume you are plotting multiple series, where each series represents a product and you are plotting per-month sales of multiples products. Although the data you passed to the chart consist of sales data (Month, Amount) for each product, you might have a separate CLR objects that represent each product. If this is the case you can specify the contenttemplate in XAML and assign in code each of your series to a specific CLR object.

2) Use Crosstab

When using crosstab we will transform a collection of Month, Product, Amount into a collection of Month, Amount, Product1, Product2, ... ProductN . In this case we will automatically use the first row where a product was found as the "representative" for such product. You can see a sample here

Hope this helps.

JuanC

post-3116-13922403273464_thumb.jpg

Link to comment
Share on other sites

 I tried most of the suggestions writen here, but all I can see changing is tooltip, but not the LegendBox items. I still see those as Point 1, Point 2 etc... or nothing at all.

How can I change those 'Point n' in the LegendBox to something meaningful?

Here is my xaml. I would like to see content of the 'Label' data element in the LegendBox. How can I do that?

<XmlDataProvider x:Key="myXmlData" XPath="ChartPoints/Point">   <x:XData>   <ChartPoints xmlns="">   <Point Value="5" Label="Consumer" />   <Point Value="7" Label="Energy"/>   <Point Value="4" Label="Technology"/>   <Point Value="3" Label="Healthcare"/>   <Point Value="7" Label="Financial" />   </ChartPoints>   </x:XData>   </XmlDataProvider>

  <ChartFX:Chart x:Name="chart1" Grid.Column="0" Grid.ColumnSpan="2" Grid.Row="2" PointCount="5" Gallery="Pie" Background="Transparent" BorderThickness="0" ItemsSource="{Binding Source={StaticResource myXmlData}}">   <ChartFX:Chart.LegendBox>   <ChartFX:LegendBox Visibility="Visible" >   </ChartFX:LegendBox>   </ChartFX:Chart.LegendBox>   <ChartFX:SeriesAttributes BindingPath="@Value" />   </ChartFX:Chart>

Thank you! 

Link to comment
Share on other sites

The key of the misunderstanding is the concepts of series and points. In ChartFX we can handle 1 or more series, each of these series will be composed by one or more points. In a line chart, each series is represented by a different line, in a pie chart each series is represented as a pie with each point being a slice.

There is also a behavior difference between bar/line/area charts and pie/doughnut/pyramid charts. In the former we default to show the series legend while the latter will display the points legend.

So to customize the items displayed in a legendbox for a pie chart you have the following options

1) If you just want to replace the default string with another string

Use the AxisX Labels, using your XAML as a sample you would add the following

<

ChartFX:Chart.AxisX>  <ChartFX:Axis>   <ChartFX:Axis.Labels>   <ChartFX:AxisLabelAttributes BindingPath="@Label"/>   </ChartFX:Axis.Labels>  </ChartFX:Axis></ChartFX:Chart.AxisX>

2) If you want to keep the marker but want to display something other than a string

Use the SeriesAttributes ContentTemplate properties. Note that in a single chart you can use the AllSeries.ContentTemplate property. NOTE: There is a bug in current builds that will interfere with series templates in a pie chart. This approach will work in builds marked version 3043 or later.

<

ChartPoints xmlns=""><Point Value="5" Label="Consumer" Image="pack://siteoforigin:,,,/Img/Consumer.png" /><Point Value="7" Label="Energy" Image="pack://siteoforigin:,,,/Img/Energy.png" /><Point Value="4" Label="Technology" Image="pack://siteoforigin:,,,/Img/Technology.png" /><Point Value="3" Label="Healthcare" Image="pack://siteoforigin:,,,/Img/Healthcare.png" /><Point Value="7" Label="Financial" Image="pack://siteoforigin:,,,/Img/Financial.png" /></ChartPoints>

Note that these images will be retrieved from an Img subdirectory of your app's folder. To assign the template you would add this to your XAML

<

ChartFX:Chart.AllSeries>  <ChartFX:AllSeriesAttributes>   <ChartFX:AllSeriesAttributes.ContentTemplate>   <DataTemplate>   <StackPanel Orientation="Horizontal">   <Image Source="{Binding XPath=@Image}" Width="24" Height="24"/>   <TextBlock Text="{Binding XPath=@Label}" Margin="2,0" VerticalAlignment="Center"/>   </StackPanel>   </DataTemplate>   </ChartFX:AllSeriesAttributes.ContentTemplate>  </ChartFX:AllSeriesAttributes></ChartFX:Chart.AllSeries>

Also note that your template will be used against the objects you pass to the chart so if you will be passing CLR objects (e.g. LinQ) you will use Bindings using Path instead of XPath

3) If you want to completely control how the legend item looks (ADVANCED)

Use the LegendItemAttributes.Template property, remember that in a pie chart we show the points legend using labels stored in the X axis so your code would be

chart1.LegendBox.ItemAttributes[chart1.AxisX].Template = (DataTemplate) FindResource("RichContentLegend");

The template will be bound to a logical object that has several properties, e.g. Fill, Stroke, etc. These are the visual properties for this slice so that you can allow the user a way to identify the item being labeled. It also exposes a DataItem property that returns the CLR object associated with each point. When binding to XAML we have found some issues if you try to use both XPath and Path in a Binding so we also expose a DataItemClr property that will synthesize an object based on your XML nodes.

<DataTemplate x:Key="MyLegendTemplate">  <StackPanel Orientation="Horizontal" Margin="0,3">   <Ellipse Fill="{Binding Path=Fill}" Width="24" Height="24" />   <Image Source="{Binding Path=DataItemClr.@Image}" Width="24" Height="24"/>   <TextBlock Text="{Binding Path=DataItemClr.@Label}" Margin="2,0" VerticalAlignment="Center"/>  </StackPanel></DataTemplate>

Regards,

JuanC

Link to comment
Share on other sites

Thank you, JuanC!

I have tried all three options and only the third one worked.

 

The first one didn't compile because AxisLabelAttributes was not found in the ChartFX assembly (the version I use is 0.8.2957.30556). And moreover Axis.Labels seems to be read-only property. Is there a newer version?

 

The second option didn't work because of the binding exception: System.Windows.Data Error: 38 : BindingExpression with XPath cannot bind to non-XML object.; XPath='@Label' BindingExpression:Path=/InnerText; DataItem='String' (HashCode=1193620881); target element is 'TextBlock' (Name=''); target property is 'Text' (type 'String') Point 5

 Do I need to specify DataContext explicitly?

 

And the third option works just fine, but it doesn't support highlighting functionality when I hover over the chart. Actually I can still trigger highlighting when I hover over the legeng box items, but they do not dim as they used to (thre chart itself does dim still). How can I get this feature back?

 

Thanks again for the quick reply and nice product! :)

VladVM. 

 

P.S. Is it possible to get an updated version of the library?  

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...