7

デスクトップのようなアイコンがあり、自由に移動できるフォームを作成しています。時には 500 以上のアイコンを表示したいので、高速に動作させる必要があります。私のアイコンは:

TMyIcon = クラス(TGraphicControl)

そのため、Windows ハンドルはありません。図面は次のとおりです。

  • 1 x Canvas.Rectangle (約 64x32)
  • 1 x Canvas.TextOut (長方形より少し小さい)
  • 1 x Canvas.Draw (画像は 32x32)

ものを移動するコードは次のようになります: MyIconMouseMove:

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

フォームには通常 50 個程度のアイコンがあり、残りは表示領域外にあります。100 個のアイコンがある場合、それらを自由に移動でき、高速に動作します。しかし、500 個のアイコンを作成すると、処理が遅くなりますが、表示されるアイコンの数は変わりません。すべてがスムーズに動作するように、Windows に非表示のアイコンを完全に無視させるにはどうすればよいですか?

または、デスクトップのようなアイコンを表示して移動できるコンポーネントがあるのでしょうか? AutoArrange = False の TShellListView のようなものですか?

4

2 に答える 2

6

TGraphicControlは、独自のハンドルを持たないコントロールです。親を使用してコンテンツを表示します。つまり、コントロールの外観を変更すると、親も再描画されます。これにより、他のすべてのコントロールの再描画がトリガーされる場合もあります。

理論的には、コントロールXが配置されている親の部分のみを無効にする必要があるため、その部分とオーバーラップするコントロールのみを再描画する必要があります。しかし、それでも、これは連鎖反応を引き起こす可能性があり、これらのコントロールの1つで1つのピクセルを変更するたびに、多くのペイントメソッドが呼び出されます。

どうやら、表示領域の外側のアイコンも塗り直されています。アイコンが表示領域の外側にある場合は、アイコンのVisibleプロパティをFalseに設定することで、これを最適化できると思います。

これが機能しない場合は、まったく異なるアプローチが必要になる場合があります。単一のコントロールにすべてのアイコンをペイントして、画像をバッファリングできるようにするオプションがあります。アイコンをドラッグしている場合は、他のすべてのアイコンをビットマップに1回ペイントできます。マウスを動かすたびに、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;

これらのコントロールの1つを作成し、フォームに入力して、10000個のアイコンで初期化します。

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

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

少し速くて汚いですが、このソリューションが非常にうまく機能する可能性があることを示しています。ドラッグ(マウスダウン)を開始すると、バッファを通過するビットマップに10000個のアイコンが描画されるため、わずかな遅延が発生します。その後、再描画ごとに2つの画像のみが描画されるため(この場合は500ではなく)、ドラッグ中に目立った遅延は発生しません。

于 2012-10-30T13:11:32.713 に答える
1

あなたはまさにあなたが求めていたものであるこのコントロールをチェックしたいかもしれません。

RMKleverからのrkView

基本的には、スクロールなどを備えたアイコンまたは写真のサムネイルビューアです。

于 2012-10-31T02:24:50.370 に答える