TGraphicControl是一种没有自己处理的控件。它使用它的父控件来显示其内容。这意味着,改变您的控件外观将强制重绘父控件。这也可能触发重新绘制所有其他控件。
理论上,只需要使控件X定位的父窗口的部分无效,因此应该只需要重绘重叠该部分的控件。但是,这可能会引起连锁反应,每次更改其中一个控件中的单个像素时都会调用许多绘制方法。
显然,还会重绘可见区域外的图标。我认为,如果图标在可见区域之外,可以通过将其Visible属性设置为False来优化此过程。
如果这样不起作用,您可能需要完全不同的方法:有一种选项可以在单个控件上绘制所有图标,从而使您可以缓冲图像。如果您正在拖动图标,则可以一次将所有其他图标绘制在位图上。在每次鼠标移动时,您只需要绘制该缓冲位图和被拖动的单个图标,而不是100(或500)个单独的图标。这应该可以加快速度,尽管需要更多的开发投入。
您可以这样实现:
type
TMyIcon = class
Pos: TPoint;
Picture: TPicture;
constructor Create(Src: TBitmap);
destructor Destroy; override;
end;
TIconList = TList;
TIconControl = class(TGraphicControl)
Icons: TIconList;
Buffer: TBitmap;
DragIcon: TMyIcon;
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
procedure Initialize;
procedure ValidateBuffer;
procedure Paint; override;
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;
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;
然后是图形控件本身:
constructor TIconControl.Create(AOwner: TComponent);
begin
inherited;
Icons := TIconList.Create;
end;
destructor TIconControl.Destroy;
begin
Icons.Free;
inherited;
end;
function TIconControl.IconAtPos(X, Y: Integer): TMyIcon;
var
r: TRect;
i: Integer;
begin
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
Src.LoadFromFile('C:\ff\ff.bmp');
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
DragIcon := IconAtPos(X, Y);
if Assigned(DragIcon) then
begin
FreeAndNil(Buffer);
Invalidate;
end;
end;
end;
procedure TIconControl.MouseMove(Shift: TShiftState; X, Y: Integer);
begin
if Assigned(DragIcon) then
begin
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
FreeAndNil(Buffer);
DragIcon := nil;
Invalidate;
end;
end;
procedure TIconControl.Paint;
begin
ValidateBuffer;
Canvas.Draw(0, 0, Buffer);
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 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张)。