我正在创建一个表格,桌面上有图标,可以自由移动。 我有时甚至会显示500个或更多图标,因此需要快速工作。 我的图标是:
TMyIcon = class(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完全忽略隐形图标,以便一切顺利进行?
或许有一个组件可以显示类似桌面的图标,可以移动它们?类似于TShellListView,AutoArrange = False?
答案 0 :(得分: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;
然后,图形控制本身:
{ 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个图标。之后,拖动时没有明显的延迟,因为每次重绘时只绘制两个图像(而不是在你的情况下为500)。
答案 1 :(得分:1)