5

I want to draw some themes parts to several TImages. In my code below, GetElementDetails expects a certain enum value. I have the PTypeInfo for the enum type, but I don't know how to type-cast i to the enum type.

procedure TForm1.Button1Click(Sender: TObject);
  procedure drawType(c: tcanvas; ti: ptypeinfo);
  var
    r: trect;
    i: integer;
    details: TThemedElementDetails;
  begin
    r.Left := 0; r.Top := 0; r.Right := 19; r.Bottom := 19;
    for i := GetTypeData(ti).MinValue to GetTypeData(ti).MaxValue do begin
      // How to cast i to the enum type of ti?
      details := StyleServices.GetElementDetails( ???(i) );

      StyleServices.DrawElement(c.Handle, details, R);
      if (i mod 10 = 0) and (i > 0) then begin
        r.Left := 0; r.Right := 19; r.Top := r.Bottom + 3; r.Bottom := r.Bottom + 22;
      end
      else r.Inflate(-22,0,22,0);
    end;
  end;
begin
  drawType(image1.canvas, typeinfo(TThemedToolBar));
  drawType(image2.canvas, typeinfo(TThemedButton));
  drawType(image3.canvas, typeinfo(TThemedCategoryPanelGroup));
  drawType(image4.canvas, typeinfo(TThemedComboBox));
end;

I need to cast i to the type passed as second variable (TThemedToolBar, TThemedButton, etc.). How can I solve this?

Rob Kennedy
  • 161,384
  • 21
  • 275
  • 467
Kiril Hadjiev
  • 302
  • 2
  • 13
  • I think that similar has been asked in [`this question`](http://stackoverflow.com/q/16433104/960757). – TLama Aug 06 '14 at 10:33
  • I can't get the enum itself in the inner function. how do you suggest to change this line: StyleServices.DrawElement(c.Handle, StyleServices.GetElementDetails(TEnumHelp.Cast(i)), R); – Kiril Hadjiev Aug 06 '14 at 10:58

2 Answers2

4

try this one:

type
   TAbstractStyleServicesFunction = reference to function(const styleServices: TAbstractStyleServices; const I: Integer)
      : TThemedElementDetails;


procedure TForm1.drawType(c: TCanvas; ti: PTypeInfo; const styleServicesFunction: TAbstractStyleServicesFunction);
var
   r: trect;
   I: Integer;
begin
   r.Left := 0;
   r.Top := 0;
   r.Right := 19;
   r.Bottom := 19;
   for I := GetTypeData(ti).MinValue to GetTypeData(ti).MaxValue do
   begin
      styleServices.DrawElement(c.Handle, styleServicesFunction(styleServices, I), r);

      if (I mod 10 = 0) and (I > 0) then
      begin
         r.Left := 0;
         r.Right := 19;
         r.Top := r.Bottom + 3;
         r.Bottom := r.Bottom + 22;
      end
      else
         r.Inflate(-22, 0, 22, 0);
   end;
end;



procedure TForm1.Button1Click(Sender: TObject);
begin
   drawType(image1.canvas, typeinfo(TThemedToolBar),
      function(const styleServices: TAbstractStyleServices; const I: Integer): TThemedElementDetails
      begin
         Result := styleServices.GetElementDetails(TThemedToolBar(I));
      end);
end;
Passella
  • 640
  • 1
  • 8
  • 23
2

You cannot do this easily. The GetElementDetails method is overloaded heavily on its first parameters. Overload resolution is static, performed at compile time. You wish to bind to the method at runtime, based on the run time type of the enum. That's not possible for normal method calls.

The only way to do this is with RTTI. Enumerate the methods of the style services object. Find all the ones that have the name GetElementDetails. Pick the one whose parameters match the run time type of your enum. Then call it using RTTI.

I can't write any code to show you this, but there are plenty of good examples of these techniques, now that you know what needs to be done.

David Heffernan
  • 601,492
  • 42
  • 1,072
  • 1,490