user3966994
user3966994

Reputation:

Dynamic TRect Draw

I am trying to simple thing. But i couldn' t :(

I have an TImage, which name is overview. I want to draw a rectangle which is on the overview but independent from overview. So i added a TImage front of the overview and drawed a rectangle. Rectangle works but i just can see the TImage or overview. I tried to giving a transparency to rectImg but rectImg completely disappear.

   with rectImg.Canvas do
      begin
        Pen.Color:= clRed;
        Rectangle(0, 0, rectImg.Width, rectImg.Height);
      end;

I draw on a paint, what i want to make. enter image description here

That rect can be resizable indepented from img.

Thanks in advice.

Upvotes: 2

Views: 1293

Answers (1)

Chris Rolliston
Chris Rolliston

Reputation: 4808

If I understand your question correctly, you effectively want to visually frame the image without drawing the frame on the original graphic itself, i.e. rectImg.Picture should not return a framed graphic. Two ways immediately come to mind:

a) Dump TImage and use TPaintBox, manually maintaining the core graphic and doing any stretching or whatever via method calls rather than property settings on the component.

b) Extend TImage to have an OnPaint event that gets raised after TImage has done its standard painting.

With respect to (b), you can do it either as an interposer class or a custom component. As an interposer class you could do this:

1) Re-declare TImage immediately above your form class:

type
  TPaintEvent = procedure (Sender: TObject; Canvas: TCanvas) of object;

  TImage = class(Vcl.ExtCtrls.TImage) //use class(ExtCtrls.TImage) if pre-XE2
  strict private
    FOnPaint: TPaintEvent;
  protected
    procedure Paint; override;
  published
    property OnPaint: TPaintEvent read FOnPaint write FOnPaint;
  end;

  TMyForm = class(TForm)
  //...

2) Implement the Paint override as so (slightly fiddly as TImage redefines the Canvas property of the base class):

type
  TGraphicControlAccess = class(TGraphicControl);

procedure TImage.Paint;
begin
  inherited;
  if Assigned(FOnPaint) then
    FOnPaint(Self, TGraphicControlAccess(Self).Canvas);
end;

3) Declare a suitable event handler in the form class:

procedure rectImgPaint(Sender: TObject; Canvas: TCanvas);

4) Implement the handler like so - note you need to set Brush.Style to bsClear to not create a filled rectangle:

procedure TMyForm.rectImgPaint(Sender: TObject; Canvas: TCanvas);
begin
  Canvas.Brush.Style := bsClear;
  Canvas.Pen.Color := clRed;
  Canvas.Rectangle(0, 0, rectImg.Width, rectImg.Height);
end;

5) Assign the event handler in the form's OnCreate event:

procedure TMyForm.ImagePaint.FormCreate(Sender: TObject);
begin
  rectImg.OnPaint := rectImgPaint;
end;

I leave converting the interposer class to a custom component as an exercise for the reader...

Postscript

Two other thoughts now I think of them:

  • Oddly enough, FMX is actually nicer here because its TImage provides a OnPaint event as standard.
  • If it is literally just a frame you want, a codeless alternative would be to overlay the TImage with a TShape, setting the shape's Brush.Style property to bsClear as we did in the coding solution. In that situation, set the shape's Enabled property to False if you have any OnClick or OnMouseXXX handlers assigned to the image.

Upvotes: 2

Related Questions