Drag&Drop. Zamiana miejscami komponentów.

0

Pochwalony.
Aktualnie piszę aplikację, w której chciałbym umożliwić użytkownikowi przesuwanie komponentów na formie.
Obsługę D&D, oraz zapisywanie i przywracanie pozycji/rozmiaru już napisałem.

Chciałbym dodać tylko zamienianie się miejscami.
Na przykład mam taką formę:
user image
Teraz, gdy użytkownik zechce przeciągnąć GroupBox3 w miejsce GroupBox1, ten ostatni powinien zająć miejsce komponentu, który zacząłem przeciągać:
user image
I tak ze wszystkimi komponentami GroupBox...

Oto mój obecny kod do obsługi D&D:

 
var
     inReposition : boolean;
     oldPos, newPos : TPoint;


procedure TMainfrm.ControlMouseDown(Sender: TObject; Button: TMouseButton;
  Shift: TShiftState; X, Y: Integer);
begin
   if
     (Sender is TWinControl) then
  begin
    inReposition:=True;
    SetCapture(TWinControl(Sender).Handle);
    GetCursorPos(oldPos);
  end;
end;

procedure TMainfrm.ControlMouseMove(Sender: TObject; Shift: TShiftState; X,
  Y: Integer);
begin
  if inReposition then
  begin
    with TWinControl(Sender) do
    begin
      GetCursorPos(newPos);
        Screen.Cursor := crSize;
        Left := Left - oldPos.X + newPos.X;
        Top := Top - oldPos.Y + newPos.Y;
        oldPos := newPos;
      end;
    end;
  end;


procedure TMainfrm.ControlMouseUp(Sender: TObject; Button: TMouseButton;
  Shift: TShiftState; X, Y: Integer);
begin
     if inReposition then
  begin
    Screen.Cursor := crDefault;
    ReleaseCapture;
    inReposition := False;
  end;
end;

Oraz procedura zapisując w OnClose aktualną pozycję komponentów:

procedure TMainfrm.ZapiszPozycje;
var
   INI : TIniFile;
   ilosc : integer;
   ctrl : TControl;
begin
   INI := TIniFile.Create(ChangeFileExt(Application.ExeName,'.ini')) ;
   try
     for ilosc := 0 to -1 + Self.ComponentCount do
     begin
       if Components[ilosc] is TControl then
       begin
         ctrl := TControl(Components[ilosc]) ;
         INI.WriteInteger(ctrl.Name,'Top',ctrl.Top) ;
         INI.WriteInteger(ctrl.Name,'Left',ctrl.Left) ;
         INI.WriteInteger(ctrl.Name,'Width',ctrl.Width) ;
         INI.WriteInteger(ctrl.Name,'Height',ctrl.Height) ;
       end;
     end;
   finally
     INI.Free ;
   end;
end;

Ktoś z Was ma jakieś pomysły/sugestie?

Pozdrawiam

0

może klik myszą + jakiś tajny szhift - zaznacza jakieś panele - dwa na przykład, następnie program kulturalnie pyta czy zamienić owe miejscami... i spokojnie działasz dalej. Bo problem może się pojawić, jak zasłonisz np kilka paneli podczas przeciągania innego, puszczasz, a tam się podmienia na nie ten co user sobie wymyślił.

0

Bardziej myślałem nad pobraniem pozycji (przykładowego) GroupBoxa, na który najechaliśmy innym, i potem zamiana ich miejscami na pdst pozycji.
Co do zasłaniania. To chyba nie będzie to problem.
Można pobrać położenie kursora podczas upuszczania i sprawdzić, czy w tym miejscu znajduje się jakiś inny komponent, który powinien obrać położenie upuszczanego komponentu..
Ale to tylko teoretyczne dywagacje...
Dzięki za sugestie...

0

zrobione w lazarusie ale powinno w delphi działać.

unit Unit1; 

{$mode objfpc}{$H+}

interface

uses
  Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, StdCtrls,
  ExtCtrls, Windows;

type

  { TForm1 }

  TForm1 = class(TForm)
    GroupBox1: TGroupBox;
    GroupBox2: TGroupBox;
    GroupBox3: TGroupBox;
    GroupBox4: TGroupBox;
    Shape1: TShape;
    Shape2: TShape;
    Shape3: TShape;
    Shape4: TShape;
    procedure FormMouseDown(Sender: TObject; Button: TMouseButton;
      Shift: TShiftState; X, Y: Integer);
    procedure FormMouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer);
    procedure FormMouseUp(Sender: TObject; Button: TMouseButton;
      Shift: TShiftState; X, Y: Integer);
  private
    { private declarations }
  public
    { public declarations }
  end; 

var
  Form1: TForm1; 

implementation

{$R *.lfm}

var
  inReposition : boolean;
  oldPos, newPos : TPoint;
  oldleft, oldtop : Integer;

{ TForm1 }

procedure TForm1.FormMouseDown(Sender: TObject; Button: TMouseButton;
  Shift: TShiftState; X, Y: Integer);
begin
  if (Sender is TWinControl) then
  begin
    oldleft:=TWinControl(Sender).Left;
    oldtop:=TWinControl(Sender).Top;
    inReposition:=True;
    SetCapture(TWinControl(Sender).Handle);
    GetCursorPos(oldPos);
  end;
end;

procedure TForm1.FormMouseMove(Sender: TObject; Shift: TShiftState; X,
  Y: Integer);
begin
  if inReposition then
  begin
    with TWinControl(Sender) do
    begin
      GetCursorPos(newPos);
      Screen.Cursor := crSize;
      Left := Left - oldPos.X + newPos.X;
      Top := Top - oldPos.Y + newPos.Y;
      oldPos := newPos;
    end;
  end;
end;

procedure TForm1.FormMouseUp(Sender: TObject; Button: TMouseButton;
  Shift: TShiftState; X, Y: Integer);
var
  i: Integer;
begin
  if inReposition then
  begin
    Screen.Cursor := crDefault;
    inReposition:=False;
    ReleaseCapture;
    for i:=0 to TWinControl(Form1).ControlCount-1 do
    begin
      if (TWinControl(sender)<>TWinControl(Form1).Controls[i]) and
         (TWinControl(sender).left >= TWinControl(Form1).Controls[i].Left) and
         (TWinControl(sender).left <=TWinControl(Form1).Controls[i].Left+TWinControl(Form1).Controls[i].Width) and
         (TWinControl(sender).Top >= TWinControl(Form1).Controls[i].Top) and
         (TWinControl(sender).Top <=TWinControl(Form1).Controls[i].Top+TWinControl(Form1).Controls[i].Height) then //można wyłapać wszystkie rogi a nie tylko pewy górny
      begin
        TWinControl(Sender).Left:=TWinControl(Form1).Controls[i].Left;
        TWinControl(Sender).Top:=TWinControl(Form1).Controls[i].Top;
        TWinControl(Form1).Controls[i].Left:=oldleft;
        TWinControl(Form1).Controls[i].Top:=oldtop;
        break;
      end;
    end;
  end;
end;

end.       
0

Dziękuję, działa. Teraz muszę tylko obmyślić plan dokładności, płynności, etc...
I można implementować.

Dzięki.

0

jesli stac Cie w projekcie na platne komponenty polecam layout control firmy developer express

1 użytkowników online, w tym zalogowanych: 0, gości: 1