Search code examples
delphichartspie-chartteechart

Delphi + TeeChart : how can I get checkbox in legend for pie serie values?


I want to allow the user to choose what values are shown in a chart with a pie serie values by displaying a check box in front of each serie's value.

There is an option to display a checkbox near each legend item but it's only working for series, not values in a serie ; and you can only have one serie of values in a single pie.

Does anyone have any idea on how to achieve this?


Solution

  • You could draw your custom legend manually. However, you should do some tricks:

    • use a dummy series to store the original values and show these values in the legend.
    • remove the values from the main series that have been clicked in the legend taking care with the indexes and the colors.

    Here an example, even with mouse Hover:

    example

    uses Series, TeCanvas, Math;
    
    var pieSeries: TPieSeries;
        dummySeries: TPieSeries;
        itemRect: array of TRect;
        itemIndex: Integer;
    
    procedure TForm1.Chart1AfterDraw(Sender: TObject);
    var i, tmpH, tmpW: Integer;
        tmpR, tmpS: TRect;
    begin
      with Chart1.Canvas do
      begin
        AssignFont(Chart1.Legend.Font);
        AssignBrush(Chart1.Legend.Brush);
    
        tmpW:=0;
        tmpH:=0;
        for i:=0 to dummySeries.Count-1 do
        begin
          tmpW:=Max(TextWidth(dummySeries.LegendString(i, Chart1.Legend.TextStyle)), tmpW);
          tmpH:=Max(TextHeight(dummySeries.LegendString(i, Chart1.Legend.TextStyle)), tmpH);
        end;
    
        Inc(tmpW, Chart1.Legend.Symbol.Width + TeeCheckBoxSize + 8);
    
        tmpR.Left:=Chart1.Width-tmpW-10;
        tmpR.Top:=50;
        tmpR.Right:=tmpR.Left+tmpW;
        tmpR.Bottom:=tmpR.Top + ((tmpH+4) * dummySeries.Count) + 4;
    
        Rectangle(tmpR);
    
        Inc(tmpR.Left, 4);
        tmpS.Left:=tmpR.Left+TeeCheckBoxSize+4;
        tmpS.Right:=tmpS.Left+TeeCheckBoxSize;
        for i:=0 to dummySeries.Count-1 do
        begin
          Brush.Color:=OperaPalette[i];
          Inc(tmpR.Top, 4);
    
          DrawCheckBox(tmpR.Left, tmpR.Top, not dummySeries.IsNull(i), clNone);
    
          if i=itemIndex then
          begin
            Pen.Color:=clRed;
            Font.Color:=clRed;
          end
          else
          begin
            Pen.Color:=Chart1.Legend.Symbol.Pen.Color;
            Font.Color:=Chart1.Legend.Font.Color;
          end;
    
          tmpS.Top:=tmpR.Top+1;
          tmpS.Bottom:=tmpS.Top+TeeCheckBoxSize;
          Rectangle(tmpS);
    
          TextOut(tmpS.Right + 2, tmpR.Top, StringReplace(dummySeries.LegendString(i, Chart1.Legend.TextStyle), TeeColumnSeparator, ' ', [rfReplaceAll, rfIgnoreCase]));
    
          itemRect[i]:=Rect(tmpR.Left, tmpS.Top, tmpR.Right, tmpS.Bottom);
    
          Inc(tmpR.Top, tmpH);
        end;
      end;
    end;
    
    procedure TForm1.Chart1Click(Sender: TObject);
    var i, j: Integer;
    begin
      if itemIndex>-1 then
      begin
         dummySeries.SetNull(itemIndex, not dummySeries.IsNull(itemIndex));
    
         pieSeries.CheckDataSource;
    
         for i:=pieSeries.Count-1 downto 0 do
           if pieSeries.IsNull(i) then
              pieSeries.Delete(i);
    
         //Fix colors
         j:=0;
         for i:=0 to dummySeries.Count-1 do
           if not dummySeries.IsNull(i) then
           begin
             pieSeries.ValueColor[j]:=OperaPalette[i];
             Inc(j);
           end;
      end;
    end;
    
    procedure TForm1.Chart1MouseMove(Sender: TObject; Shift: TShiftState; X,
      Y: Integer);
    var i, j: Integer;
    begin
      itemIndex:=-1;
      for i:=0 to length(itemRect)-1 do
        if PointInRect(itemRect[i], X, Y) then
        begin
          itemIndex:=i;
          break;
        end;
    
      if itemIndex>-1 then
      begin
        if dummySeries.IsNull(itemIndex) then
           pieSeries.Selected.HoverIndex:=-1
        else
        begin
          j:=0;
          for i:=0 to itemIndex-1 do
            if not dummySeries.IsNull(i) then
              Inc(j);
    
          pieSeries.Selected.HoverIndex:=j;
        end;
      end;
    
      Chart1.CancelMouse:=True;
      Chart1.Repaint;
    end;
    
    procedure TForm1.FormCreate(Sender: TObject);
    var i: Integer;
    begin
      pieSeries:=Chart1.AddSeries(TPieSeries) as TPieSeries;
      pieSeries.FillSampleValues;
    
      for i:=0 to pieSeries.Count-1 do
        pieSeries.ValueColor[i]:=OperaPalette[i];
    
      dummySeries:=CloneChartSeries(pieSeries) as TPieSeries;
      dummySeries.ParentChart:=nil;
    
      pieSeries.DataSource:=dummySeries;
    
      Chart1.Legend.Visible:=False;
      Chart1.MarginRight:=20;
    
      SetLength(itemRect, dummySeries.Count);
    
      itemIndex:=-1;
    end;