You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Delphi FMX平台PaintBox Paint方法无法更新画布问题求助

Fixing FMX PaintBox Update Issue in Cross-Platform VNC Project

Hey there, let's tackle this FMX PaintBox problem you're facing—first, let's break down why the weird behavior happens, then fix it properly.

Why Resizing the Window Updates the PaintBox?

FMX controls automatically trigger a repaint (via Invalidate → Repaint) when their size changes. That's why adjusting your ScreenView form makes the latest frame appear: the resize forces the pbViewPaint event to run again, drawing your updated Bitmap to the canvas. Your core drawing logic works—you just haven't told the PaintBox to redraw when new images arrive.

What's Wrong with the Current Code?

There are two key issues holding you back:

  1. Incorrect Canvas Usage in FMX Paint Event:
    Your modified FMX paint event uses pbView.Canvas instead of the Canvas parameter passed to the event. In FMX, the parameterized Canvas is the valid, prepared drawing context for the current paint cycle. Using pbView.Canvas directly can lead to drawing not being rendered correctly, especially outside of the paint event.

  2. Missing Explicit Repaint Trigger:
    VCL sometimes implicitly marks controls as needing repaint after certain operations, but FMX is stricter. When your Client receives a new image and updates the Bitmap, the PaintBox has no idea it needs to redraw—so it stays stuck on the first frame. The Timer hack works because it forces a repaint, but it's inefficient and unstable.

Step-by-Step Fixes

1. Correct the Paint Event Code

Update your pbViewPaint to use the provided Canvas parameter instead of the control's direct Canvas property:

procedure TFrmScreenView.pbViewPaint(Sender: TObject; Canvas: TCanvas);
begin
  Client.DrawBitmap(Canvas); // Use the event-provided Canvas, not pbView.Canvas
end;

2. Trigger Repaint When Bitmap Updates

You need to tell the PaintBox to redraw as soon as the Bitmap is updated. The cleanest way is to add an event to your TClient class that fires when the bitmap changes, then call Repaint on the PaintBox (must be done on the main thread, since FMX controls aren't thread-safe).

First, add an event to TClient:

type
  TBitmapUpdatedEvent = procedure(Sender: TObject) of object;

  TClient = class
  private
    FOnBitmapUpdated: TBitmapUpdatedEvent;
    // ... your existing fields
  public
    property OnBitmapUpdated: TBitmapUpdatedEvent read FOnBitmapUpdated write FOnBitmapUpdated;
    // ... your existing methods
  end;

Then, modify the code where you update the Bitmap in TClient to trigger the event:

procedure TClient.UpdateBitmap(NewBitmap: TBitmap);
begin
  if Assigned(Bitmap) then
    Bitmap.Free;
  Bitmap := TBitmap.Create;
  Bitmap.Assign(NewBitmap);
  
  // Notify the UI that the bitmap has changed
  if Assigned(FOnBitmapUpdated) then
    FOnBitmapUpdated(Self);
end;

Finally, bind the event in your form and trigger the repaint safely on the main thread:

procedure TFrmScreenView.FormCreate(Sender: TObject);
begin
  Client.OnBitmapUpdated := ClientBitmapUpdated;
end;

procedure TFrmScreenView.ClientBitmapUpdated(Sender: TObject);
begin
  // Use TThread.Queue to run the repaint on the main thread
  TThread.Queue(nil, procedure
  begin
    pbView.Repaint;
  end);
end;

3. Ditch the Timer

Remove the Timer you were using—this new approach triggers a repaint only when a new frame arrives, which is far more efficient and reliable than periodic forced repaints.

Why This Works

  • Using the event-provided Canvas ensures your drawing uses the correct FMX rendering context, eliminating invisible or incorrect draws.
  • Triggering Repaint immediately after the Bitmap updates tells the PaintBox it needs to redraw right away, so you get real-time updates without relying on window resizing or a hacky Timer.

内容的提问来源于stack exchange,提问作者Aras

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.08 21:52:48