2012-10-29 10 views
7

Tworzę formularz, w którym znajdują się ikony - na przykład na pulpicie i mogą być swobodnie przenoszone. Chcę pokazywać czasami nawet 500 lub więcej ikon, więc muszą szybko pracować. Moja ikona:Dlaczego forma z 500 komponentami jest wolna?

TMyIcon = class (TGraphicControl)

więc nie posiada uchwyt Windows. Rysunek jest:

  • 1 x Canvas.Rectangle (co stanowi około 64x32)
  • 1 x Canvas.TextOut (trochę mniejszą niż prostokąt)
  • 1 x Canvas.Draw (obraz jest 32x32)

Kod przenieść rzeczy jest tak: MyIconMouseMove:

Ico.Left := Ico.Left + X-ClickedPos.X; 
Ico.Top := Ico.Top + Y-ClickedPos.Y; 

Na formularzu jest zwykle około 50 ikon, reszta znajduje się poza widocznym obszarem. Gdy mam 100 ikon - mogę je swobodnie przenosić i działa szybko. Ale kiedy tworzę 500 ikon, staje się on laggy, ale liczba widocznych ikon jest wciąż taka sama. Jak mogę nakazać systemowi Windows całkowite ignorowanie niewidocznych ikon, aby wszystko działało sprawnie?

A może jest jakiś komponent, który może wyświetlać ikony podobne do pulpitu z możliwością ich przemieszczania? Coś jak TShellListView z AutoArrange = False?

+7

Dlaczego to jest coraz downvotes? –

+2

Zamiast opisywać kod, możesz wyświetlić kod. –

+4

@Mason Wheeler: myślałem tak samo. Obudzenie jest w porządku, jeśli pytanie naprawdę jest do bani (nie w tym przypadku), ale osoby prowadzące opuszczanie boiska powinny co najmniej pozostawić konstruktywny komentarz, aby opisać, jak można poprawić to pytanie. –

Odpowiedz

6

TGraphicControl to kontrola, która nie ma własnego uchwytu. Korzysta z rodzica, aby wyświetlić jego zawartość. Oznacza to, że zmiana wyglądu kontrolki zmusi również do przerysowania rodzica. Może to również spowodować odświeżenie wszystkich innych elementów sterujących.

Teoretycznie należy unieważnić tylko część elementu nadrzędnego, w którym znajduje się element sterujący X, dlatego tylko te elementy, które nakładają się na tę część, powinny zostać przemalowane. Ale nadal może to powodować reakcję łańcuchową, powodując wywoływanie wielu metod malowania za każdym razem, gdy zmieniasz pojedynczy piksel w jednym z tych elementów sterujących.

Wygląda na to, że również ikony poza widocznym obszarem są odmalowane. Myślę, że możesz to zoptymalizować, ustawiając właściwość Visible dla ikon na False, jeśli znajdują się poza widocznym obszarem.

Jeśli to nie zadziała, możesz potrzebować zupełnie innego podejścia: istnieje możliwość pomalowania wszystkich ikon na jednym elemencie sterującym, co umożliwia buforowanie obrazów. Jeśli przeciągasz ikonę, możesz pomalować wszystkie pozostałe ikony na mapie bitowej raz. Przy każdym ruchu myszki wystarczy namalować tę zbuforowaną bitmapę i pojedynczą ikonę, która jest przeciągnięta, zamiast 100 (lub 500) oddzielnych ikon. To powinno trochę przyspieszyć, chociaż trochę wysiłku będzie trzeba się rozwinąć.

Można wdrożyć go tak:

type 
    // A class to hold icon information. That is: Position and picture 
    TMyIcon = class 
    Pos: TPoint; 
    Picture: TPicture; 
    constructor Create(Src: TBitmap); 
    destructor Destroy; override; 
    end; 

    // A list of such icons 
    //TIconList = TList<TMyIcon>; 
    TIconList = TList; 

    // A single graphic controls that can display many icons and 
    // allows dragging them 
    TIconControl = class(TGraphicControl) 
    Icons: TIconList; 
    Buffer: TBitmap; 
    DragIcon: TMyIcon; 

    constructor Create(AOwner: TComponent); override; 
    destructor Destroy; override; 

    procedure Initialize; 
    // Painting 
    procedure ValidateBuffer; 
    procedure Paint; override; 
    // Dragging 
    function IconAtPos(X, Y: Integer): TMyIcon; 
    procedure MouseDown(Button: TMouseButton; Shift: TShiftState; 
     X, Y: Integer); override; 
    procedure MouseMove(Shift: TShiftState; X, Y: Integer); override; 
    procedure MouseUp(Button: TMouseButton; Shift: TShiftState; 
     X, Y: Integer); override; 
    end; 


{ TMyIcon } 

// Some random initialization 
constructor TMyIcon.Create(Src: TBitmap); 
begin 
    Picture := TPicture.Create; 
    Picture.Assign(Src); 
    Pos := Point(Random(500), Random(400)); 
end; 

destructor TMyIcon.Destroy; 
begin 
    Picture.Free; 
    inherited; 
end; 

Następnie graphiccontrol sam:

{ TIconControl } 

constructor TIconControl.Create(AOwner: TComponent); 
begin 
    inherited; 
    Icons := TIconList.Create; 
end; 

destructor TIconControl.Destroy; 
begin 
    // Todo: Free the individual icons in the list. 
    Icons.Free; 
    inherited; 
end; 

function TIconControl.IconAtPos(X, Y: Integer): TMyIcon; 
var 
    r: TRect; 
    i: Integer; 
begin 
    // Just return the first icon that contains the clicked pixel. 
    for i := 0 to Icons.Count - 1 do 
    begin 
    Result := TMyIcon(Icons[i]); 
    r := Rect(0, 0, Result.Picture.Graphic.Width, Result.Picture.Graphic.Height); 
    OffsetRect(r, Result.Pos.X, Result.Pos.Y); 
    if PtInRect(r, Point(X, Y)) then 
     Exit; 
    end; 
    Result := nil; 
end; 


procedure TIconControl.Initialize; 
var 
    Src: TBitmap; 
    i: Integer; 
begin 
    Src := TBitmap.Create; 
    try 
    // Load a random file. 
    Src.LoadFromFile('C:\ff\ff.bmp'); 

    // Test it with 10000 icons. 
    for i := 1 to 10000 do 
     Icons.Add(TMyIcon.Create(Src)); 

    finally 
    Src.Free; 
    end; 
end; 

procedure TIconControl.MouseDown(Button: TMouseButton; Shift: TShiftState; X, 
    Y: Integer); 
begin 
    if Button = mbLeft then 
    begin 
    // Left button is clicked. Try to find the icon at the clicked position 
    DragIcon := IconAtPos(X, Y); 
    if Assigned(DragIcon) then 
    begin 
     // An icon is found. Clear the buffer (which contains all icons) so it 
     // will be regenerated with the 9999 not-dragged icons on next repaint. 
     FreeAndNil(Buffer); 

     Invalidate; 
    end; 
    end; 
end; 

procedure TIconControl.MouseMove(Shift: TShiftState; X, Y: Integer); 
begin 
    if Assigned(DragIcon) then 
    begin 
    // An icon is being dragged. Update its position and redraw the control. 
    DragIcon.Pos := Point(X, Y); 

    Invalidate; 
    end; 
end; 

procedure TIconControl.MouseUp(Button: TMouseButton; Shift: TShiftState; X, 
    Y: Integer); 
begin 
    if (Button = mbLeft) and Assigned(DragIcon) then 
    begin 
    // The button is released. Free the buffer, which contains the 9999 
    // other icons, so it will be regenerated with all 10000 icons on 
    // next repaint. 
    FreeAndNil(Buffer); 
    // Set DragIcon to nil. No icon is dragged at the moment. 
    DragIcon := nil; 

    Invalidate; 
    end; 
end; 

procedure TIconControl.Paint; 
begin 
    // Check if the buffer is up to date. 
    ValidateBuffer; 

    // Draw the buffer (either 9999 or 10000 icons in one go) 
    Canvas.Draw(0, 0, Buffer); 

    // If one ican was dragged, draw it separately. 
    if Assigned(DragIcon) then 
    Canvas.Draw(DragIcon.Pos.X, DragIcon.Pos.Y, DragIcon.Picture.Graphic); 
end; 

procedure TIconControl.ValidateBuffer; 
var 
    i: Integer; 
    Icon: TMyIcon; 
begin 
    // If the buffer is assigned, there's nothing to do. It is nilled if 
    // it needs to be regenerated. 
    if not Assigned(Buffer) then 
    begin 
    Buffer := TBitmap.Create; 
    Buffer.Width := Width; 
    Buffer.Height := Height; 
    for i := 0 to Icons.Count - 1 do 
    begin 
     Icon := TMyIcon(Icons[i]); 
     if Icon <> DragIcon then 
     Buffer.Canvas.Draw(Icon.Pos.X, Icon.Pos.Y, Icon.Picture.Graphic); 
    end; 
    end; 
end; 

tworzą jeden z tych kontroli, sprawiają, że wypełnienie formularza i zainicjować go z 10000 ikon.

procedure TForm1.FormCreate(Sender: TObject); 
begin 
    DoubleBuffered := True; 

    with TIconControl.Create(Self) do 
    begin 
    Parent := Self; 
    Align := alClient; 
    Initialize; 
    end; 
end; 

To trochę szybkie & brudne, ale pokazuje to rozwiązanie może działać bardzo dobrze. Jeśli zaczniesz przeciągać (myszką w dół), zauważysz małe opóźnienie podczas rysowania 10000 ikon na bitmapie, która przechodzi dla bufora. Po tym nie ma zauważalnego opóźnienia podczas przeciągania, ponieważ na każdym odmalowaniu rysowane są tylko dwa obrazy (zamiast 500 w twoim przypadku).

+1

Nie miałem czasu po południu, ale teraz dodałem mały przykład, który pokazuje, jak zarządzać 10000 ikonami w jednym kontrolerze, z rozsądnym szybkim przeciąganiem i upuszczaniem. – GolezTrol

+0

Dzięki temu wygląda obiecująco. Nie można go skompilować w Łazarzu ani Delphi 2005 z powodu takich rzeczy jak: TList , ale może uda mi się zrobić, jeśli będzie użyteczny po kilku zmianach. – Tom

+0

Ah, próbowałem użyć nowszych funkcji, nie znając twojej wersji Delphi. 'TList ' oznacza 'listę TMyIcons'. W Delphi 2005 możesz po prostu użyć TList, tylko będziesz musiał nieco zmienić pętlę 'for' i wpisać elementy do TMyIcon po pobraniu. – GolezTrol

1

Możesz chcieć sprawdzić tę kontrolę, o którą dokładnie prosiłeś.

rkView from RMKlever

To jest w zasadzie ikona lub zdjęcie przeglądarka miniatur z przewijanie itp

+1

Wygląda obiecująco, ale nie ma wersji demonstracyjnej, nie ma dokumentacji i nie jest tylko zamiennikiem ListView, więc nie wiem jak go jeszcze użyć. – Tom

+1

To naprawdę wymaga PROSTEGO demo. Może zrobię jeden, ponieważ pan Klever był tak miły, że mógł kontrolować. Ma demo, ale to strasznie trudne demo, aby zbudować i pracować: http://rmklever.com/?p=318 –

+0

Znalazłem jego inne demo: http://rmklever.com/?p=266 ale to jest również dość skomplikowany. Szczerze mówiąc odwiedziłem tę stronę kilka razy w przeszłości, pobrałem komponenty, ale z powodu braku pakietów, demonstracji, mnóstwa zależności nigdy nie znalazłem czasu na skompilowanie czegoś z tego. – Tom