Skip to main content Skip to footer

C1Chart : Trendline Selection with Mouse

In the Product Samples we have a sample that allows individual data series to be selected with a mouse click and the selected series is highlighted. However, there is no direct way to do the same with trendlines. In this blog, we will see how we can select different trendlines with a mouse click and highlight them. We will use the default chart data which is available when you drag n drop C1Chart control on the form. Since trendlines are not created by default, we need to create them manually and this can be done with the following code:

private void AddChartTrendLines(ChartData chartData)  
{  
    for (int si = 0; si < chartData.SeriesList.Count; si++ )  
    {  
         ChartDataSeries cds = chartData.SeriesList[si];  
         TrendLine tl = chartData.TrendsList.AddNewTrendLine();  
         tl.SeriesIndex = si;  
         tl.TrendLineType = TrendLineTypeEnum.Polynom;  
         tl.RegressionOptions.NumTerms = 4;  

         Color c = cds.LineStyle.Color;  
         tl.LineStyle.Color = Color.FromArgb((c.R + 255) / 2, (c.G + 255) / 2, (c.B + 255) / 2);  
         tl.LineStyle.Thickness = 3;  
    }  
 }

The concept used for finding the trendline is the same as in the SelectSeries sample, i.e. given the nearest mouse coordinate, we need to find the index of closest trendline less than the specified number of pixels distant. An index of -1 indicates no trendline within the specified number of pixels. Since there is no direct method which returns the index of nearby trendline, we will write a custom method for the same. Here is the code:

private int FindNearestTrendline(ChartGroup cg, Rectangle brect, Point p, int maxPixelDistance)  
{  
    int tlIndex = -1;  

    // if the point value lies within the chart data coordinate region  
    // there could be a trendline near  
    if (brect.Contains(p))  
    {  
       ChartData cd = cg.ChartData;  
       TrendLinesCollection tlc = cg.ChartData.TrendsList;  

       // find the minimum and maximum x pixel values within range  
       int pixMinX = ((p.X - maxPixelDistance) >= brect.Left) ? (p.X - maxPixelDistance) : brect.Left;  
       int pixMaxX = ((p.X + maxPixelDistance) <= brect.Right) ? (p.X + maxPixelDistance) : brect.Right;  

       // minimum square is enough to identify the closest  
       float maxDistSquared = maxPixelDistance * maxPixelDistance;  
       float minDistSquared = float.MaxValue;  

       for (int ti = 0; ti < tlc.Count; ti++)  
       {  
          TrendLine tl = tlc[ti];  
          float xf = 0f, yf = 0f;  

          // for each x pixel value, determine the trendline y pixel value  
          for (int px = pixMinX; px <= pixMaxX; px++)  
          {  
             double xv = 0, yv = 0;  
             if (cg.CoordToDataCoord(px, p.Y, ref xv, ref yv))  
             {  
                   // get the trendline y value for the calculated x value  
                   yv = tl.GetY(xv);  

                   // now get the pixel x and y values  
                   if (cg.DataCoordToCoord(xv, yv, ref xf, ref yf))  
                   {  
                      // calculate and compare minimum distance squared values  
                      float minds = (xf - p.X) * (xf - p.X) + (yf - p.Y) * (yf - p.Y);  
                      if (minds < maxDistSquared && minds < minDistSquared)  
                      {  
                         // smallest min distance squared value so far and within range.  
                         tlIndex = ti;  
                         minDistSquared = minds;  
                         if (minds == 0f) break;        // will not get closer than zero  
                      }  
                   }  
               }  
           }  
       }  
    }  
    return tlIndex;  
 }

After getting the index of selected trendline with the help of the above method, we will now highlight this trendline. This can be done with the following code:

private void processMouseOp(object sender, MouseEventArgs e)  
{  
    ChartGroup cg = c1Chart1.ChartGroups.Group0;  
    Rectangle brect = new Rectangle(c1Chart1.ChartArea.PlotArea.Location,  
    c1Chart1.ChartArea.PlotArea.Size);  
    int maxPixelDistance = 3;  

    int tlIndex = FindNearestTrendline(cg, brect, e.Location, maxPixelDistance);  

    if (tlIndex != lastHighlightedTrendLineIndex)  
    {  
       TrendLinesCollection tlc = cg.ChartData.TrendsList;  
       int last = lastHighlightedTrendLineIndex;  
       if (tlIndex >= 0 && tlIndex < tlc.Count)  
       {  
          TrendLine tl = tlc[tlIndex];  
          tl.LineStyle.Thickness *= 2;  
          lastHighlightedTrendLineIndex = tlIndex;  
       }  
       else  
          lastHighlightedTrendLineIndex = -1;  

       if (last >= 0 && last <= tlc.Count)  
       {  
          TrendLine tl = tlc[last];  
          tl.LineStyle.Thickness /= 2;  
       }  
       // Show status label  
       if (tlIndex < 0)  
          label1.Text = "Selection: none";  
       else  
          label1.Text = "Selection: " + cg.ChartData.TrendsList[tlIndex].SeriesLabel +" TrendLine" ;  
    }  
}

Refer to the complete sample implementing the same: Download C# Sample
Download VB Sample

MESCIUS inc.

comments powered by Disqus