0

We are having a VCL TPaintBox on which we display video. Its parent is a TScrollbox.

When zooming, the PaintBox gets larger than the scrollbox, so parts of the image is hidden.

We have implemented the ability to drag the paintbox, using the mouse, within the scrollbox, which works fine.

However, whenever the paintBox is moved, i.e. its left or top property is changed, its content gets erased.

Question is, is there a way to prevent the paintbox to erase its content while moving it?

I created a simple VCL Forms example with a paintbox inside a scroll box, code below. When clicking on the keyboard, a line is painted on the paintbox.

The example shows, that without constant painting, the content of the paintbox gets erased upon moving itself. Question is, how to avoid, or deal with?

#ifndef Unit1H
#define Unit1H
#include <System.Classes.hpp>
#include <Vcl.Controls.hpp>
#include <Vcl.StdCtrls.hpp>
#include <Vcl.Forms.hpp>
#include <Vcl.ExtCtrls.hpp>
#include "RzButton.hpp"

class TForm1 : public TForm
{
    __published:
        TPaintBox *PB;
        TScrollBox *ScrollBox1;
        void __fastcall ObjMouseDown(TObject *Sender, TMouseButton     Button, TShiftState Shift, int X, int Y);
        void __fastcall ObjMouseMove(TObject *Sender, TShiftState Shift, int X, int Y);
        void __fastcall ObjMouseUp(TObject *Sender, TMouseButton Button,  TShiftState Shift, int X, int Y);
        void __fastcall PBPaint(TObject *Sender);
        void __fastcall FormKeyDown(TObject *Sender, WORD &Key, TShiftState Shift);

    public:
        __fastcall TForm1(TComponent* Owner);

        bool mIsDragging;
        TPoint mMouseDownLocation;
        TPoint mDragStartingLocation;
};
extern PACKAGE TForm1 *Form1;
#endif

and source

#include <vcl.h>
#pragma hdrstop
#include "Unit1.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma link "RzButton"
#pragma resource "*.dfm"
TForm1 *Form1;
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{}
//---------------------------------------------------------------------------
void __fastcall TForm1::ObjMouseDown(TObject *Sender, TMouseButton Button, TShiftState Shift,
      int X, int Y)
{
    mIsDragging = true;
    mMouseDownLocation = Mouse->CursorPos;
    mDragStartingLocation = TPoint(PB->Left, PB->Top);
}

//---------------------------------------------------------------------------
void __fastcall TForm1::ObjMouseMove(TObject *Sender, TShiftState Shift,
      int X, int Y)
{
     if(mIsDragging)
     {
        PB->Left = mDragStartingLocation.X + (Mouse->CursorPos.X - mMouseDownLocation.X);
        PB->Top = mDragStartingLocation.Y  + (Mouse->CursorPos.Y - mMouseDownLocation.Y);
     }
}

//---------------------------------------------------------------------------
void __fastcall TForm1::ObjMouseUp(TObject *Sender, TMouseButton Button,
      TShiftState Shift, int X, int Y)
{
     mIsDragging = false;
}

//---------------------------------------------------------------------------
void __fastcall TForm1::PBPaint(TObject *Sender)
{
//    PB->Canvas->Pen->Color = clRed;
//    PB->Canvas->Pen->Width = 3;
//    PB->Canvas->MoveTo(50,50);
//    PB->Canvas->LineTo(150, 150);
}

//---------------------------------------------------------------------------
void __fastcall TForm1::FormKeyDown(TObject *Sender, WORD &Key, TShiftState Shift)
{
  PB->Canvas->Pen->Color = clRed;
  PB->Canvas->Pen->Width = 3;
  PB->Canvas->MoveTo(50,50);
  PB->Canvas->LineTo(150, 150);
}
Totte Karlsson
  • 1,261
  • 1
  • 20
  • 55
  • Please show you actual code. Even if the PaintBox did go blank, I would expect the next video frame to redraw it. Which implies there is a problem with your video display. – Remy Lebeau Mar 10 '22 at 22:45
  • Good point - I'll add to the question. However, the video frame rate is really slow, like 1 frame per second, so, the code stops updating the paintbox while the box being dragged, as otherwise, its really flickery. – Totte Karlsson Mar 10 '22 at 22:52
  • Dragging the PaintBox around should not stop repaints from happening. You should still be getting `OnPaint` events. However, there is one flaw in your example. Anything you draw on the PaintBox from outside the `OnPaint` event (as you are in the `OnKeyDown` event) will be lost on the next repaint (which you are forcing with `Invalidate()`). Anything you want the PaintBox to display MUST be drawn in the `OnPaint` event only. If you need something more persistent, consider using `TImage` instead of `TPaintBox`. – Remy Lebeau Mar 10 '22 at 23:23
  • Yes. And so question is how to prevent that. To keep what is painted when moving the box, if possible. And even if I remove Invalidate, its 'repainted'. I guess I need to make a copy of the current content, put it in a TImage and show it while dragging.. – Totte Karlsson Mar 10 '22 at 23:28
  • You can't prevent it. You are forcing a repaint on every move, but even if you didn't force it, a repaint is still likely to happen. You must redraw the entire PaintBox UI on every `OnPaint` event. DO NOT draw on the PaintBox's `Canvas` from outside of the `OnPaint` event - period. So, in your example, you MUST re-draw the line in `PBPaint()`, which you currently have that logic commented out. – Remy Lebeau Mar 10 '22 at 23:29
  • 1
    If your video frame rate is really that slow, you are going to have to cache the current frame and re-draw it every time `OnPaint` is fired (which can happen multiple times between frames), updating the cache and repainting on each new frame. So yes, I would probably use `TImage` *instead of* `TPaintBox` for this, let the `TImage`'s own `Picture->Bitmap` act as that cache for you. Every time you get a new frame, simply redraw the `Bitmap`, the repainting will be handled automatically for you. – Remy Lebeau Mar 10 '22 at 23:34

0 Answers0