Grafika
2.08.1999
  • Jak pozbyć się migotania TImage przy przesuwaniu.

       Problem z obiektem TImage polega na tym, że wywodzi się on z klasy TGraphicControl (ta zaś z TControl), co oznacza, że nie posiada on własnego okna. TImage, TLabel i TShape - wszystkie one pochodzą od TGraphicControl. Poruszanie tym typem kontrolek (poprzez zmianę właściwości Top i Left) będzie powodowało wyraźne migotanie. Dzieje się tak, ponieważ wymagają one ciągłego odświeżania przy wyświetlaniu w nowym miejscu (w przeciwieństwie do obiektów wywodzących się od TWinControl). Windows zna pozycję TWinControl, ale nie wie nic o TGraphicControl.

      Można tą sytuację porównać do tablicy na notatki: kontrolki TWinControl są przyczepione szpilkami, zaś TGraphicControl są po prostu narysowane na tablicy. Jest o wiele prościej przesunąć kontrolkę "przyszpiloną", aniżeli narysowaną. Widzisz w tym sens? Najłatwiejsze więc rozwiązanie to zmiana rodzica (Parent) kontrolki TGraphicControl na TWinControl i przesunięcie WinControl (np. panel). Kiedy używasz panelu, ustaw właściwość FullRepaint na false. Oto przykład poruszania obiektu TImage (w zasadzie poruszania panelu...)

      Strategia: tworzymy TPanel (który jest TWinControl), umieszczamy na nim TImage podczas przeciągania i z powrotem na formie po przeciągnięciu. Idąc dalej, zablokujemy wymazywanie tła poprzez "subclassing" metody WindowProc panelu.

//w pliku nagłówkowym

private:	
bool FDragging;
    int OldX, OldY;
    TPanel *TempPanel;
    Controls::TWndMethod OldPanelWP;
    void __fastcall NewPanelWP(TMessage &Msg);


//w pliku źródłowym
//-----------------------------------------------------
__fastcall TForm1::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;
    //poprzez "subclassing" metody WindowProc
    //blokujemy komunikat WM_ERASEBKGND
    OldPanelWP = TempPanel->WindowProc;
    TempPanel->WindowProc = NewPanelWP;
}

void __fastcall TForm1::NewPanelWP(TMessage &Msg)
{
   if (Msg.Msg == WM_ERASEBKGND)
   {
Msg.Result = 0;
        return;
   }
   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;
}