Mick Posted December 28, 2009 Report Share Posted December 28, 2009 How do I create a chart with multiple Y Axes? For example, I want to create a bar chart that is scaled by the Y axis on the left and overlay it with a line chart that scales by the axis on the right. I also want both the bar series and the line series to use the same legend (e.g. red/yellow/blue all correspond to the same legend entry). How do I do this? Thank you, Mick Quote Link to comment Share on other sites More sharing options...
Mick Posted December 29, 2009 Author Report Share Posted December 29, 2009 To clarify, I know that this relates to the post 12-21-2009 by another user, but what I'm really concerned with is the second part of my question (how to align 2 separate series to the same legend item). Thank you, Mick Quote Link to comment Share on other sites More sharing options...
CarlosAC Posted January 4, 2010 Report Share Posted January 4, 2010 Hi Mick Unfortunately, you cannot assign 2 different series to the same legend item. As a workaround, you can clear all default items and create your own new legend items. Following the code showing how to clear and add new items. // Clear the default items. LegendItemAttributes lia = new LegendItemAttributes(); lia = chart1.LegendBox.ItemAttributes[(ILegendItemGenerator)chart1.Series]; lia.Visibility = Visibility.Hidden; // Add your custom legend item. CustomLegendItem myCustomItem1 = new CustomLegendItem(); myCustomItem1.Content = "New Label 1"; myCustomItem1.Marker.Shape = MarkerShape.Circle; myCustomItem1.Fill = new SolidColorBrush(Colors.LightSteelBlue); chart1.LegendBox.CustomItems.Add(myCustomItem1); -- Pipon Quote Link to comment Share on other sites More sharing options...
Mick Posted January 4, 2010 Author Report Share Posted January 4, 2010 Thank you, In response to the workaround: 1) As an alternative to the code posted above, Is the following code safe to use? I don't want to have to manually track the color of any of the series - I'd rather let the ChartFX engine take care of it for me - I reuse ChartFX's code that generates the legend item of the correct color automatically: ... // Create the series and add them to the chartSeriesAttributes sa1 = new SeriesAttributes { Gallery = Gallery.Bar };SeriesAttributes sa2 = new SeriesAttributes { Gallery = Gallery.Line };chart1.Series.Add(sa1);chart1.Series.Add(sa2);// Set the colors of the second series to match the color of the first series automaticallysa2.Stroke = sa1.Stroke;sa2.Fill = sa1.Fill; // Collapse the second series (line) so we don't see it anymore in the legendchart1LegendBox.ItemAttributes[myChart.Series, 1].Visibility = Visibility.Collapsed;... 2) As an alternative, is it possible to hook into an event to a mouse hover on either the series or the legend? I want to take advantage of your fancy hilight feature, and if I have to, I will track what needs to hilight at any given time. With the code I posted, I get the hover feature with 1 out of 2 series - because the legend item is linked to one (but not both) of the series. --- For clarity: Yours: (Mouse hovered over bar or line) Mine: (Mouse hovered over bar or legend) (Mouse hovered over line) Ideal: (Mouse hovered over bar, line or legend) Thank you, MIck Quote Link to comment Share on other sites More sharing options...
JuanC Posted January 5, 2010 Report Share Posted January 5, 2010 I think we have the appropriate hooks for this to work but I would like to understand more about your scenario, can you describe (or post a sample) what your data look like and what the series legend items represent? JuanC Quote Link to comment Share on other sites More sharing options...
Mick Posted January 5, 2010 Author Report Share Posted January 5, 2010 I used random data to populate this chart, but it shows what I'm trying to get at (what the lines and bars represent): file:///C:/Users/MICKUL%7E1/AppData/Local/Temp/moz-screenshot.jpg Both of the charts (bar and line) represent "performance" in one way or another. The bars represent each individual year.The line represents the cumulative total.---The reason I only want 1 legend item is that in a real world scenario, I have more than one series displayed for each bar+line graph. Because both the line and the bar apply to "Percent," it seems to make sense that they correspond to the same legend item. The following just seems redundant: I know that the graph is cluttered, but it would be nice to be able to mouse-over the "light blue" legend item that applied to BOTH the bar and the line. That way, the blue bars and the blue line would highlight so I could easily see them both. See the 4 screen shots and the descriptions next to them for what I want the bars+lines+legend to look like during each scenario. Thank you, Mick Quote Link to comment Share on other sites More sharing options...
JuanC Posted January 6, 2010 Report Share Posted January 6, 2010 There are 2 ways of achieving what you are looking for, first let's initialize our chart int numberOfSeries = 3; chart1.Series.Clear(); for (int i = 0; i < numberOfSeries; i++) chart1.Series.Add(new SeriesAttributes() { Gallery = Gallery.Bar }); for (int i = 0; i < numberOfSeries; i++) chart1.Series.Add(new SeriesAttributes() { Gallery = Gallery.Line }); The first approach uses PointAttributes which allow you to set visual attributes on a per-point basis, our highlighting code will then try to highlight all elements sharing the same point attributes // Aproach 1: Using Point Attributes // This works well if you do not need additional per-point attributes and the number of points is not too big int numberOfPoints = 5; for (int i = 0; i < numberOfSeries; i++) { SeriesAttributes seriesSource = chart1.Series; PointAttributes pointAttr = new PointAttributes(); pointAttr.Fill = seriesSource.Fill; pointAttr.Stroke = seriesSource.Stroke; pointAttr.Marker.Fill = seriesSource.Fill; pointAttr.Marker.Stroke = seriesSource.Stroke; // Replace this with your series description // if you do not set the PointAttributes content it will not appear in the legend box pointAttr.Content = string.Format("Series {0}", i + 1); for (int j = 0; j < numberOfPoints; j++) { chart1.Points[i, j] = pointAttr; chart1.Points[i + numberOfSeries, j] = pointAttr; } } chart1.LegendBox.ItemAttributes[chart1.Series].Visibility = Visibility.Collapsed; The drawback is that you have to assign the point attributes for all points on each series although we are sharing the same instance. When we are drawing there will be a small performance hit as we will "search" a dictionary to see if you have set attributes for a specific series/point combination. For a small data set this should not be noticeable. The second approach is based on building your own Highlight Resolver that decides what gets highlighted/dimmed, Note that we still have to make sure (if desired) that visual attributes are shared between the line and the bar for (int i = 0; i < numberOfSeries; i++) { SeriesAttributes seriesSource = chart1.Series; SeriesAttributes seriesTarget = chart1.Series[i + numberOfSeries]; seriesTarget.Fill = seriesSource.Fill; seriesTarget.Stroke = seriesSource.Stroke; seriesTarget.Marker.Fill = seriesSource.Fill; seriesTarget.Marker.Stroke = seriesSource.Stroke; chart1.LegendBox.ItemAttributes[chart1.Series, i + numberOfSeries].Visibility = Visibility.Collapsed; } chart1.Highlight.CreatingResolver += new HighlightResolverEventHandler(OnCreatingResolver); private void OnCreatingResolver (object sender, HighlightingEventArgs args) { IRequiresIndex requiresIndex = null; LogicalVisualItem visualItem = args.Object as LogicalVisualItem; if (visualItem != null) requiresIndex = visualItem.Series; if (requiresIndex != null) { int seriesIndex = requiresIndex.Index; args.Resolver = new MyResolver(seriesIndex, 3); } } private class MyResolver : IHighlightResolver { private int m_targetSeries; private int m_groupCount; public MyResolver(int targetSeries, int groupCount) { m_targetSeries = targetSeries; m_groupCount = groupCount; } void IHighlightResolver.ApplyRemove (Chart chart, bool apply) { } object IHighlightResolver.HighlightData { get { return null; } } HighlightState IHighlightResolver.IsItemHighlighted (ILogicalHighlightableItem item, bool apply) { IRequiresIndex requiresIndex = item.Series; if (requiresIndex == null) return HighlightState.Normal; int series = requiresIndex.Index; if ((series % m_groupCount) == (m_targetSeries % m_groupCount)) return HighlightState.Highlighted; else return HighlightState.Dimmed; } HighlightState IHighlightResolver.IsObjectHighlighted (object item, IPlotArea plotArea) { return HighlightState.Normal; } bool IHighlightResolver.RequiresRemoval (IHighlightResolver resolver) { return true; } ResolverStyles IHighlightResolver.Style { get { return ResolverStyles.PerSeries; } } } For this code to compile you have to add using clauses for ChartFX.WPF.Internal and ChartFX.WPF.Internal.Highlight JuanC Quote Link to comment Share on other sites More sharing options...
Recommended Posts
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.