Skip to main content

How to Custom Paint Table Views

  • 4 minutes to read

Custom painting allows much deeper appearance customization compared to the capabilities provided by the Spreadsheet and Report Designer controls’ API. Handle a OnCustomDraw~ event corresponding to a specific worksheet area or visual element(s) to custom paint a Table View worksheet:

Custom painting can be useful for expanding the control with custom functionality. For example, you want to show formula calculation error hints to provide an end-user with more informative notifications than the standard error codes displayed within the formula cell. The example below illustrates how you can implement custom paint tasks in these cases.

First, implement the callout painting procedure that displays an error code hint:

procedure DrawCallout(ACanvas: TcxCanvas; AViewInfo: TdxSpreadSheetTableViewCellViewInfo; AError: TdxSpreadSheetFormulaErrorCode);
var
  AClipRgn, AFrameRgn: TcxRegion;
  FramePoints: array[0..5] of TPoint;
  AMessage: UnicodeString;
  AHOffset: Integer;
begin
  case AError of  // Pick an appropriate callout message according to the formula error code
    ecNull:
      begin
        AMessage := 'Place separators between specified cell ranges';
        AHOffset := 320;
      end;
    ecDivByZero:
      begin
        AMessage := 'Division by zero: check formula arguments';
        AHOffset := 300;
      end;
    ecValue:
      begin
        AMessage := 'Wrong types of arguments';
        AHOffset := 200;
      end;
    ecRefErr:
      begin
        AMessage := 'Invalid cell reference';
        AHOffset := 200;
      end;
    ecName:
      begin
        AMessage := 'Formula is not recognized';
        AHOffset := 200;
      end;
    ecNUM:
      begin
        AMessage := 'Invalid numeric argument';
        AHOffset := 200;
      end;
    ecNA:
      begin
        AMessage := 'Result is not found';
        AHOffset := 180;
      end
    else
      begin
        AMessage := 'OK';
        AHOffset := 50;
      end;
  end;
  ATopLeft.x = AViewInfo->DisplayBounds.left;
  ATopLeft.y = AViewInfo->DisplayBounds.top;
  FramePoints[0] := cxPointOffset(ATopLeft, 0, 10);
  FramePoints[1] := cxPointOffset(FramePoints[0], 5, -10);
  FramePoints[2] := cxPointOffset(FramePoints[1], AHOffset, 0);
  FramePoints[3] := cxPointOffset(FramePoints[2], 0, -20);
  FramePoints[4] := cxPointOffset(FramePoints[3], -AHOffset, 0);
  FramePoints[5] := FramePoints[0];
  AFrameRgn := TcxRegion.Create;
  AFrameRgn.Handle := CreatePolygonRgn(FramePoints, Length(FramePoints), WINDING);
  ACanvas.SaveState;
  AClipRgn := TcxRegion.Create(AViewInfo.ViewInfo.CellsArea);
  AClipRgn.Combine(AFrameRgn, roIntersect, False);
  ACanvas.SetClipRegion(AClipRgn, roSet, False);
  FillRegionByColor(ACanvas.Handle, AFrameRgn.Handle, clInfoBk);
  FrameRgn(ACanvas.Handle, AFrameRgn.Handle, GetStockObject(BLACK_BRUSH), 1, 1);
  ACanvas.Font.Name := 'Tahoma';
  ACanvas.Font.Size := 10;
  ACanvas.Font.Color := clBlack;
  ACanvas.Font.Style := [fsBold];
  ACanvas.Brush.Style := bsClear;
  ACanvas.DrawTexT(AMessage, cxRect(FramePoints[4], FramePoints[2]),
    cxAlignVCenter or cxAlignHCenter or cxSingleLine);
  ACanvas.RestoreState;
  ACanvas.SetClipRegion(AClipRgn, roSubtract);
  AFrameRgn.Free;
end;

Then, call the DrawCallout procedure within the OnCustomDrawTableViewCell event handler:

procedure SpreadSheetCustomDrawTableViewCell(Sender: TdxSpreadSheetTableView; ACanvas: TcxCanvas; AViewInfo: TdxSpreadSheetTableViewCellViewInfo; var AHandled: Boolean);
var
  AErrorCode: TdxSpreadSheetFormulaErrorCode;
begin
  if (AViewInfo.Cell = nil) or (AViewInfo.Cell.DataType <> cdtFormula) then Exit;
  AErrorCode := AViewInfo.Cell.AsFormula.ErrorCode;
  if (AViewInfo.Cell.AsFormula.ErrorCode <> ecNone) or
   (not VarIsNumeric(AViewInfo.Cell.AsFormula.Value) or (AViewInfo.Cell.AsFormula.Value < 0)) then
    begin
      ACanvas.Brush.Color := clRed;
      ACanvas.Font.Color := clAqua;
      DrawCallout(ACanvas, AViewInfo, AErrorCode);
    end;
end;

As a result, the Spreadsheet/Report Designer control displays formula error hints in callout boxes. The image below shows the division by zero error hint: