TGraphicControl è un controllo che non ha una maniglia propria. Usa i suoi genitori per mostrare il suo contenuto. Ciò significa che cambiare l'aspetto del tuo controllo costringerà anche il padre a essere ridisegnato. Ciò potrebbe anche innescare la riverniciatura di tutti gli altri controlli.
In teoria, solo la parte del genitore su cui è posizionato il controllo X deve essere invalidata, quindi è necessario ridipingere solo i controlli che si sovrappongono a tale parte. Tuttavia, ciò potrebbe causare una reazione a catena, provocando la chiamata di molti metodi di pittura ogni volta che si modifica un singolo pixel in uno di questi controlli.
Apparentemente, anche le icone all'esterno dell'area visibile vengono ridipinte. Penso che tu possa ottimizzare questo impostando la proprietà Visible delle icone su False se sono al di fuori dell'area visibile.
Se questo non funziona, potrebbe essere necessario un approccio completamente diverso: c'è la possibilità di dipingere tutte le icone su un singolo controllo, consentendo di bufferizzare le immagini. Se stai trascinando un'icona, puoi dipingere tutte le altre icone su una bitmap una volta. In ogni mossa del mouse, devi solo dipingere quella bitmap con buffer e l'icona singola che viene trascinata, invece di 100 (o 500) icone separate. Ciò dovrebbe accelerare un po 'le cose, anche se ci vorrà un po' più di sforzo per svilupparsi.
si potrebbe implementare in questo modo:
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;
Poi, il graphiccontrol stesso:
{ 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;
Creare uno di quei controlli, rendono compilare il modulo e inizializzare con 10000 icone.
procedure TForm1.FormCreate(Sender: TObject);
begin
DoubleBuffered := True;
with TIconControl.Create(Self) do
begin
Parent := Self;
Align := alClient;
Initialize;
end;
end;
E 'un po' rapida & sporca, ma dimostra questa soluzione può funzionare molto bene. Se inizi a trascinare (il mouse verso il basso), noterai un piccolo ritardo mentre le icone 10000 vengono disegnate sulla bitmap che passa per un buffer. Dopo di ciò, non vi è alcun ritardo notevole durante il trascinamento, poiché su ogni ridisegno vengono disegnate solo due immagini (anziché 500 nel tuo caso).
Perché questo ottenendo downvotes? –
Piuttosto che descrivere il codice, potresti mostrare il codice. –
@Mason Wheeler: stavo pensando lo stesso. Il downvoting va bene se una domanda fa davvero schifo (non è il caso qui), ma i downvoters dovrebbero almeno lasciare un commento costruttivo per descrivere come la domanda potrebbe essere migliorata. –