0

I'm looking into GDI+ in Delphi, and found the demo below. (Can't remember where.) It's quite straightforward, and works fine on the first click: a series of circles is drawn side by side on a classical Delphi TImage and a GDI+ controlled TImage.
Subsequent clicks, however, do produce new circles in Image1, but not in Image2 (the GDI+ image). What's happening here?

procedure TFormMain.Button1Click(Sender: TObject);
var
  I, X, Y, R: Integer;
  graphics: TGPGraphics;
  SolidPen: TGPPen;
  SolidBrush: TGPBrush;
begin
  graphics := TGPGraphics.Create(Image2.Canvas.Handle);
  SolidPen := TGPPen.Create(MakeColor(255, 0, 0, 0), 3);
  SolidBrush := TGPSolidBrush.Create(MakeColor(255, 0, 0, 0));
  try
    graphics.SetSmoothingMode(SmoothingModeAntiAlias);
    SolidPen.SetWidth(3);
    Image1.Canvas.Pen.Width := 3;
    for I := 1 to 1000 do
    begin
      R := Random(100);
      X := Random(700);
      Y := Random(1000);
      Image1.Canvas.Ellipse(X-R, Y-R, X+R, Y+R);

      (SolidBrush as TGPSolidBrush).SetColor($80000000 or Random($1000000));
      graphics.FillEllipse(SolidBrush, X-R, Y-R, 2*R, 2*R);
      graphics.DrawEllipse(SolidPen, X-R, Y-R, 2*R, 2*R);
    end;
  finally
    SolidBrush.Free;
    SolidPen.Free;
    graphics.Free;
  end;
end;
stevenvh
  • 2,971
  • 9
  • 41
  • 54
  • You mustn't paint outside the `WM_PAINT` handler. – Andreas Rejbrand Apr 13 '23 at 18:29
  • @Andreas -- thanks for your comment, and it gave me an idea for a solution. But you make me question the quality of the demo: is there a better way to do it? – stevenvh Apr 13 '23 at 18:40
  • I haven't worked much with GDI+, but with ordinary GDI (and most other Win32-based graphics approaches), you must only paint in a window in its `WM_PAINT` handler. It is simply wrong to do it outside such a handler. In particular, it is wrong to do it in an `OnClick` handler. Here are a few examples of how you should draw (only in the `OnPaint` handler): https://stackoverflow.com/a/72912274/282848, https://stackoverflow.com/a/7224075/282848 If you want to force a redraw, call `Invalidate`. That will cause Windows to create a `WM_PAINT` message to you. – Andreas Rejbrand Apr 13 '23 at 18:51
  • @Andreas -- Thanks again. Is Invalidate better here than Refresh? – stevenvh Apr 13 '23 at 18:57
  • 2
    `Invalidate` tells Windows that the window needs to be repainted. Then Windows will create a `WM_PAINT` message as soon as appropriate (typically within a millisecond or two). And if you call `Invalidate` several times (because you change several aspects of the image, say), you will still only get a single redraw. `Refresh`, on the other hand, causes an immediate redraw (even if it isn't a convenient time for the system), and if you call `Refresh` several times in succession, you get several unnecessary repaints (you redraw the same frame many times). So if you do everything the right... – Andreas Rejbrand Apr 13 '23 at 19:10
  • ...way (i.e., only painting inside `WM_PAINT` (`OnPaint`)), then yes: `Invalidate` is much better. – Andreas Rejbrand Apr 13 '23 at 19:11

1 Answers1

0

Andreas' comment on the WM_PAINT gave me an idea: I added

Image2.Invalidate

after the loop, which solves the problem.


Note: changed the Refresh I wrote originally to Invalidate, upon Andreas' suggestion.

stevenvh
  • 2,971
  • 9
  • 41
  • 54