2012-10-29 40 views
7

我正在創建一個有桌面圖標的表單,並且可以自由移動。 我想顯示有時甚至500或更多的圖標,以便他們需要快速工作。 我的圖標是:爲什麼一個包含500個組件的表單很慢?

TMyIcon = A類(TGraphicControl)

所以它並沒有Windows句柄。 附圖是:

  • 1×Canvas.Rectangle(其爲約64×32)
  • 1×Canvas.TextOut(有點比矩形小)
  • 1×Canvas.Draw(圖像是32×32)

移動的東西的代碼是這樣的: MyIconMouseMove:

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

在窗體上通常會有50個左右的圖標 - 其餘部分位於可見區域之外。 當我有100個圖標時,我可以自由移動它,並且運行速度很快。但是當我創建500個圖標時,它會變得遲緩 - 但可見圖標的數量仍然相同。 如何告訴Windows完全忽略無形圖標,以便一切順利運行?

或者,也許有一個組件可以顯示類似桌面的圖標,並可以移動它們?像TShellListView與AutoArrange = False?

+7

這是爲什麼越來越downvotes? –

+2

而不是描述代碼,你能顯示代碼嗎? –

+4

@Mason Wheeler:我在想同樣的事情。如果一個問題真的很糟糕(如果不是這裏的情況),那麼降低投票就沒有問題,但是投降者至少應該留下建設性的意見來描述如何改進問題。 –

回答

6

TGraphicControl是一個沒有自己的句柄的控件。它使用其父項來顯示其內容。這意味着,改變控件的外觀也會強制重繪父項。這也可能會觸發重新繪製所有其他控件。

理論上,只有控制X所在的父母部分需要失效,因此只有重疊該部分的控件需要重新繪製。但是,這可能會導致連鎖反應,每次更改其中一個控件中的單個像素時,都會調用大量繪畫方法。

顯然,可見區域外的圖標也被重新繪製。我認爲你可以通過將圖標的Visible屬性設置爲False來優化它,如果它們在可見區域之外的話。

如果這不起作用,您可能需要一種完全不同的方法:可以選擇在單個控件上繪製所有圖標,以便緩衝圖像。如果您拖動圖標,則可以在位圖上繪製所有其他圖標一次。在每次鼠標移動時,您只需要繪製緩衝的位圖和拖動的單個圖標,而不是100(或500)個單獨的圖標。這應該能夠加快事情的速度,儘管它需要更多的努力來開發。

您可以實現這樣的:

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; 

然後,graphiccontrol本身:

{ 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; 

創建這些控件之一,使其填寫表格,並與10000個圖標的初始化。

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

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

這是一個有點快&髒,但它顯示了這個解決方案可以很好地工作。如果您開始拖動(鼠標向下),您會注意到一小段延遲,因爲10000圖標繪製在傳遞緩衝區的位圖上。之後,拖動時不會有明顯的延遲,因爲每次重繪時只繪製兩張圖像(而不是您的情況)。

+1

今天下午我沒有時間,但現在我已經添加了一個小例子,展示瞭如何在一個控件中管理10000個圖標,並且可以快速拖放。 – GolezTrol

+0

謝謝你,看起來很有希望。不能在Lazarus和Delphi 2005中編譯它,因爲這樣的事情:TList 但是也許我可以在經過一些更改後使用。 – Tom

+0

啊,我試圖使用更新的功能,不知道你的Delphi版本。 'TList '表示'TMyIcons列表'。在Delphi 2005中,您可以使用TList,只需要稍微更改'for'循環,並在檢索時將項目類型轉換爲TMyIcon。 – GolezTrol

1

你可能想看看這個控制,這正是你所要求的。

rkView from RMKlever

它基本上是一個圖標或照片縮略圖瀏覽器與滾動等

+1

看起來很有前途,但沒有演示,沒有文檔,它不只是一個ListView替代品,所以我不知道如何使用它。 – Tom

+1

它確實需要一個簡單的演示。也許我會做一個,因爲克萊弗先生非常好,可以控制。他有一個演示,但這是一個非常艱難的演示,以建立和工作:http://rmklever.com/?p=318 –

+0

我發現他的另一個演示:http://rmklever.com/?p=266但它也很複雜。說實話,我過去曾幾次訪問過這個網站,下載了這些組件,但由於沒有包,演示,很多依賴關係,我從來沒有時間將它編譯出來。 – Tom