Eliminating flicker during a TImage move operations 

The problem with TImage is that it descends from TGraphicControl (which in turn descends from TControl) which means that it does not reside inside it's own window. TImage, TLabel, and TShape all derive from TGraphicControl. Moving these types of controls around (by changing the Top and Left properties) will most likely cause flicker as you may already have noticed.  This is because when moved, they need constant repainting (unlike TWinControl descendants which don't need constant repainting when moved) to be displayed in the new location.  Windows itself knows the location of window controls, but knows nothing about
TGraphicControls.  It very analogous to having a posterboard:  TWinControls are pinned to the posterboard, while TGraphicControls are drawn on the posterboard.  It's real easy to move the pinned controls around by simply unpinning them then changing their location.  The controls that are drawn directly to the posterboard need to be erased then redrawn.  Does this make sense?  So the easiest thing to do is Parent the GraphicControl descendant on a WinControl, and move the WinControl (such as a TPanel).  Using a TPanel component, be sure to set the FullRepaint property to false.  Here's an example of moving a TImage around (actually moving the Panel).

KEYWORDS: flicker, TImage, TWinControl 
 


The strategy here is to create a TPanel which is indeed a TWinControl, and place the image on that during dragging, and place it back on the Form after dragging.  To take it even further, we'll subclass the WindowProc of the Panel to supress background erasing.  So here's what you do... 

 

//in header file 
private
    bool FDragging; 
    int OldX, OldY; 
    TPanel *TempPanel;

    Controls::TWndMethod OldPanelWP;
    void __fastcall NewPanelWP(TMessage &Msg);

 


 
 

//---------------------------------------------------------------------------
__fastcallTForm1::TForm1(TComponent* Owner) 
        : TForm(Owner) 

    FDragging = false
    TempPanel = new TPanel(this); 
    TempPanel->Parent = this
    TempPanel->BorderStyle = bsNone; 
    TempPanel->BevelInner = bvNone; 
    TempPanel->BevelOuter = bvNone; 
    TempPanel->FullRepaint = false; //remove for BCB1 (false by default)

  //subclass the WindowProc to supress the WM_ERASEBKGND message
    OldPanelWP = TempPanel->WindowProc;
    TempPanel->WindowProc = NewPanelWP;
}

void __fastcall TForm1::NewPanelWP(TMessage &Msg)
{
  if (Msg.Msg != WM_ERASEBKGND) Msg.Result = false;
  else OldPanelWP(Msg);
}
 

void __fastcall TForm1::Image1MouseDown(TObject *Sender, TMouseButton
Button, TShiftState Shift, int X, int Y) 

    FDragging = true
    OldX = X; 
    OldY = Y; 

    TempPanel->Left = Image1->Left; 
    TempPanel->Top = Image1->Top; 
    TempPanel->Height = Image1->Height; 
    TempPanel->Width = Image1->Width; 
    TempPanel->Visible = true
    SetCapture(TempPanel->Handle); 

    Image1->Parent = TempPanel; 
    Image1->Left = 0; 
    Image1->Top = 0; 

 

void __fastcall TForm1::Image1MouseMove(TObject *Sender, TShiftState
Shift, int X, int Y) 

  if (FDragging == true
    { 
        TempPanel->Left = TempPanel->Left + (X - OldX); 
        TempPanel->Top = TempPanel->Top + (Y - OldY); 
    } 

 

void __fastcall TForm1::Image1MouseUp(TObject *Sender, TMouseButton
Button, TShiftState Shift, int X, int Y) 

    TempPanel->Visible = false
    Image1->Left = TempPanel->Left; 
    Image1->Top = TempPanel->Top; 
    Image1->Parent = this
    FDragging = false

 

void __fastcall TForm1::FormClose(TObject *Sender, TCloseAction
&Action) 

    TempPanel->WindowProc = OldPanelWP;
  delete TempPanel;