Jump to content
Software FX Community

Multiple Y Axes


Mick

Recommended Posts

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

Link to comment
Share on other sites

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

Link to comment
Share on other sites

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 chart
SeriesAttributes 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 automatically
sa2.Stroke = sa1.Stroke;
sa2.Fill = sa1.Fill;

// Collapse the second series (line) so we don't see it anymore in the legend
chart1LegendBox.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:

Posted Image (Mouse hovered over bar or line)

Mine:

Posted Image (Mouse hovered over bar or legend)

Posted Image (Mouse hovered over line)

Ideal:

Posted Image (Mouse hovered over bar, line or legend)

Thank you,

MIck

Link to comment
Share on other sites

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.jpgPosted Image

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:

Posted Image 

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

Link to comment
Share on other sites

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

 

 

 

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...