TGraphicControl to kontrola, która nie ma własnego uchwytu. Korzysta z rodzica, aby wyświetlić jego zawartość. Oznacza to, że zmiana wyglądu kontrolki zmusi również do przerysowania rodzica. Może to również spowodować odświeżenie wszystkich innych elementów sterujących.
Teoretycznie należy unieważnić tylko część elementu nadrzędnego, w którym znajduje się element sterujący X, dlatego tylko te elementy, które nakładają się na tę część, powinny zostać przemalowane. Ale nadal może to powodować reakcję łańcuchową, powodując wywoływanie wielu metod malowania za każdym razem, gdy zmieniasz pojedynczy piksel w jednym z tych elementów sterujących.
Wygląda na to, że również ikony poza widocznym obszarem są odmalowane. Myślę, że możesz to zoptymalizować, ustawiając właściwość Visible dla ikon na False, jeśli znajdują się poza widocznym obszarem.
Jeśli to nie zadziała, możesz potrzebować zupełnie innego podejścia: istnieje możliwość pomalowania wszystkich ikon na jednym elemencie sterującym, co umożliwia buforowanie obrazów. Jeśli przeciągasz ikonę, możesz pomalować wszystkie pozostałe ikony na mapie bitowej raz. Przy każdym ruchu myszki wystarczy namalować tę zbuforowaną bitmapę i pojedynczą ikonę, która jest przeciągnięta, zamiast 100 (lub 500) oddzielnych ikon. To powinno trochę przyspieszyć, chociaż trochę wysiłku będzie trzeba się rozwinąć.
Można wdrożyć go tak:
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;
Następnie graphiccontrol sam:
{ 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;
tworzą jeden z tych kontroli, sprawiają, że wypełnienie formularza i zainicjować go z 10000 ikon.
procedure TForm1.FormCreate(Sender: TObject);
begin
DoubleBuffered := True;
with TIconControl.Create(Self) do
begin
Parent := Self;
Align := alClient;
Initialize;
end;
end;
To trochę szybkie & brudne, ale pokazuje to rozwiązanie może działać bardzo dobrze. Jeśli zaczniesz przeciągać (myszką w dół), zauważysz małe opóźnienie podczas rysowania 10000 ikon na bitmapie, która przechodzi dla bufora. Po tym nie ma zauważalnego opóźnienia podczas przeciągania, ponieważ na każdym odmalowaniu rysowane są tylko dwa obrazy (zamiast 500 w twoim przypadku).
Dlaczego to jest coraz downvotes? –
Zamiast opisywać kod, możesz wyświetlić kod. –
@Mason Wheeler: myślałem tak samo. Obudzenie jest w porządku, jeśli pytanie naprawdę jest do bani (nie w tym przypadku), ale osoby prowadzące opuszczanie boiska powinny co najmniej pozostawić konstruktywny komentarz, aby opisać, jak można poprawić to pytanie. –