Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   GUI-Design mit VCL / FireMonkey / Common Controls (https://www.delphipraxis.net/18-gui-design-mit-vcl-firemonkey-common-controls/)
-   -   Delphi Drag and drop (https://www.delphipraxis.net/16128-drag-drop.html)

Hansa 10. Feb 2004 23:04


Drag and drop
 
Hi,

habe hier >2 Listboxen. 8) Schiebe damit Zeilen hin und her. Jetzt kommts:

Delphi-Quellcode:
procedure TForm1.ListBox4DragDrop(Sender, Source: TObject; X, Y: Integer);
begin
  if Source is TListBox then with Sender do begin
    Text := ListBox2.Items[ListBox2.ItemIndex];
    ListBox4.AddItem(Text,source);
    ListBox2.Items.Delete (ListBox2.ItemIndex);
  end;
end;

soweit, so gut, aber wie kann ich das allgemeingültig machen? Da steht jetzt explizit Listbox2 oder Listbox4 drin und so geht das denn auch, aber was, falls ListBoxAssarbad :mrgreen: da steht und aus Listbox1 soll eine Zeile da rein ?

Bernd Ua 10. Feb 2004 23:38

Re: Drag and drop
 
wenn Absender und Source die Listboxen wären, so etwa :

Delphi-Quellcode:
procedure TForm1.ListBox4DragDrop(Sender, Source: TObject; X, Y: Integer);
var
  lbSource,lbSender : TListBox;
begin
  lbSource := Source as TListBox;
  lbSender := Sender as TListbox;
  lbsender.Items.Add(lbSource.Items[lbSource.ItemIndex];
  lbSource.Items.Delete (lbSource.ItemIndex);
end;
Bernd

P.S Was dein addItem da bezweckt und mit Zeilen hin und herschieben zu tun hat wüsste ich ja auch mal gerne :mrgreen:

Hansa 11. Feb 2004 10:48

Re: Drag and drop
 
Thx, Dein Code ist etwas übersichtlicher, als meiner und auch kleiner. :mrgreen: So gefällts mir besser. Nur noch ein Schönheitsfehler, zumindest aus meiner Sicht 8) :

Delphi-Quellcode:
procedure TForm1.ListBox2DragOver(Sender, Source: TObject; X, Y: Integer;
  State: TDragState; var Accept: Boolean);
begin
  Accept := Source is TListBox;
end;

procedure TForm1.ListBox2DragDrop(Sender, Source: TObject; X, Y: Integer);
var
  lbSource,lbSender : TListBox;
begin
  lbSource := Source as TListBox;
  lbSender := Sender as TListbox;
  lbsender.Items.Add(lbSource.Items[lbSource.ItemIndex]);
  lbSource.Items.Delete (lbSource.ItemIndex);
end;

procedure TForm1.ListBox2MouseDown(Sender: TObject; Button: TMouseButton;
  Shift: TShiftState; X, Y: Integer);
begin
  if button = mbLeft then with Sender as TListBox do begin
    if ItemAtPos (Point (x,y),true) >= 0 then
      BeginDrag (false);
  end;
end;
Dieser Code steht jetzt 4mal im Programm (für 4 Listboxen). Es ist also immer derselbe, bis auf den Prozedur-Namen. Wie kann ich den verallgemeinern oder besser noch zur Laufzeit auswerten, je nachdem, wieviele Listboxen ich im Endeffekt brauche ?

Mir fällt gerade ein, daß ich das ganze auf die Form übertragen könnte. Aber das DragDrop der Form hat kein accept. So wirds wohl nicht gehen, oder was muß ich da hin schreiben ?

Delphi-Quellcode:
procedure TForm1.FormDragDrop(Sender, Source: TObject; X, Y: Integer);
begin

end;

Hansa 11. Feb 2004 11:13

Re: Drag and drop
 
Das war jetzt auch verkehrt. Der Code sieht jetzt so aus:

Delphi-Quellcode:
procedure TForm1.FormDragOver(Sender, Source: TObject; X, Y: Integer;
  State: TDragState; var Accept: Boolean);
begin
  Accept := true; //Source is TListBox;
end;

procedure TForm1.FormDragDrop(Sender, Source: TObject; X, Y: Integer);
var
  lbSource,lbSender : TListBox;
begin
  lbSource := Source as TListBox;
  lbSender := Sender as TListbox;
  lbsender.Items.Add(lbSource.Items[lbSource.ItemIndex]);
  lbSource.Items.Delete (lbSource.ItemIndex);
end;

procedure TForm1.FormMouseDown(Sender: TObject; Button: TMouseButton;
  Shift: TShiftState; X, Y: Integer);
begin
  if button = mbLeft then with Sender as TListBox do begin
    if ItemAtPos (Point (x,y),true) >= 0 then
      BeginDrag (false);
  end;
end;
Wieso geht das so nicht ? Gefordert ist : egal wie viele Listboxen da sind, jede Zeile aus einer soll in eine beliebige andere verschoben werden können.

Bernd Ua 11. Feb 2004 11:53

Re: Drag and drop
 
Wenn dein Code nur sender/Source verwendet kannst du doch OnDragDrop von
beliebig vielen Listboxen auf ein und dieselbe Methdoe zeigen lassen.
Das mit dem Form brauchst du nicht.

Bernd

Hansa 11. Feb 2004 12:46

Re: Drag and drop
 
Das ist ja auch immer dasselbe, aber es geht nur wenn ich ListBox1..4Dragdrop einzeln aufrufe. Wie meinst du, soll ich das alles auf eine procedure zeigen lassen ? :shock:

MikeS 11. Feb 2004 13:11

Re: Drag and drop
 
funktioniert das auch nicht, wenn du die allgemeinen von dir angelegten proceduren
dem jeweiligen ereignis deiner listboxen zuweißt?

also listbox2.dragdrop = form1.dragdrop <- oder so ähnlich!

oder im objectinspektor einstellen

Hansa 11. Feb 2004 13:41

Re: Drag and drop
 
Die Teilfrage bleibt offen, ich lebe dann eben vorerst mit zu vielen unnötigen Prozeduren. Jetzt sehe ich gerade, daß die Listbox ja auch Columns hat. Wie behandele ich denn die ? Ich bräuchte 2 Columns pro Listbox und wollte eventuell ein Stringgrid verwenden. Vielleicht tuts die Listbox aber doch. Wie gesagt, die Hauptfrage ist die Verwendung von Drag and Drop.

Hansa 11. Feb 2004 13:50

Re: Drag and drop
 
Und da ist schon das nächste Problem. :mrgreen: Wenn ich Multiselect auf true stelle kann ich mit SHIFT mehrere Elemente auswählen. Sobald ich die Maus benutze um alle zu verschieben ist nur noch eines markiert. Was fehlt da noch ?

Hansa 13. Feb 2004 18:39

Re: Drag and drop
 
Scheint keinen mehr zu interessieren. :gruebel:

Ich habe das ganze jetzt mal statt mit einer Listbox mit einem StringGrid versucht:

Delphi-Quellcode:
unit StrTestUnit;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, Grids, StdCtrls;

type
  TStringGridExt = class(TStringGrid)
  protected
    procedure InsertRow(ARow: Longint);
    procedure DeleteRow(ARow: Longint);
  end;

type
  TForm2 = class(TForm)
    StringGrid1: TStringGrid;
    Button1: TButton;
    Button2: TButton;
    StringGrid2: TStringGrid;
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
    procedure StringGrid1DragOver(Sender, Source: TObject; X, Y: Integer;
      State: TDragState; var Accept: Boolean);
    procedure StringGrid1DragDrop(Sender, Source: TObject; X, Y: Integer);
    procedure StringGrid1MouseDown(Sender: TObject; Button: TMouseButton;
      Shift: TShiftState; X, Y: Integer);
  private
    {...}
  public
    {...}
  end;

var
  Form2: TForm2;

implementation

{$R *.DFM}

procedure TStringGridExt.InsertRow(ARow: Longint);
var
  TempRow: Integer;
begin
  TempRow := Row;            // Zeile zwischenspeichern
  while ARow < FixedRows do
    ARow := ARow + 1;
  RowCount := RowCount + 1;
  MoveRow(RowCount - 1, ARow);
  Row := TempRow;
  Rows[Row].Clear;
end;

procedure TStringGridExt.DeleteRow(ARow: Longint);
var
  GemRow: Integer;
begin
  GemRow := Row;
  if RowCount > FixedRows + 1 then
    inherited DeleteRow(ARow)
  else
    Rows[ARow].Clear;
  if GemRow < RowCount then Row := GemRow;
end;

procedure TForm2.Button1Click(Sender: TObject);
begin
  TStringGridExt(StringGrid1).InsertRow(1);
end;

procedure TForm2.Button2Click(Sender: TObject);
begin
  TStringGridExt(StringGrid1).DeleteRow(1);
end;

procedure TForm2.StringGrid1DragOver(Sender, Source: TObject; X, Y: Integer;
  State: TDragState; var Accept: Boolean);
begin
  Accept := true; //Source is TStringGridExt;
end;

procedure TForm2.StringGrid1DragDrop(Sender, Source: TObject; X,
  Y: Integer);
var
  lbSource,lbSender : TStringGridExt;
begin
  lbSource := Source as TStringGridExt;
  lbSender := Sender as TStringGridExt;
  lbsender.InsertRow (lbSender.Row);
  lbSource.DeleteRow (lbSource.Row);
end;

procedure TForm2.StringGrid1MouseDown(Sender: TObject;
  Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
begin
  if button = mbLeft then with Sender as TStringGridExt do begin
    if ItemAtPos (Point (x,y),true) >= 0 then
      BeginDrag (false);
  end;
end;

end.
Das StringGridExt enthält 2 Prozeduren, um Zeilen zu löschen / einzufügen. Ansonsten ist es ähnlich, wie bei der Lsitbox. Nur die MouseDown Prozedur wird nicht compiliert, da es kein ItemAtPos im StringGrid gibt. Nur, was soll ich statt dessen verwenden ??

Keldorn 13. Feb 2004 18:49

Re: Drag and drop
 
Hallo

Zitat:

ItemAtPos im StringGrid gibt. Nur, was soll ich statt dessen verwenden ??
Stringgrid.mousetocell?

Zitat:

Und da ist schon das nächste Problem. Wenn ich Multiselect auf true stelle kann ich mit SHIFT mehrere Elemente auswählen. Sobald ich die Maus benutze um alle zu verschieben ist nur noch eines markiert. Was fehlt da noch ?
die vorherigen Codes fügen immer nur das ausgewählte hinzu (itemindex), klappere alle ausgewählten Items in einer Schleife ab.

Zitat:

ich lebe dann eben vorerst mit zu vielen unnötigen Prozeduren.
mir ist immer noch nicht ganz klar, wieso du es nicht schaffst, allen listboxen ein und dieselbe Proceduren im OI zuzuweisen. Bei mir ging das prima.

Mfg Frank

Hansa 13. Feb 2004 20:46

Re: Drag and drop
 
Hi Keldorn,

die zweite Antwort von Dir wollte ich so umsetzen:

Delphi-Quellcode:
procedure TForm1.ListBox2DragDrop(Sender, Source: TObject; X, Y: Integer);
var
  i : integer;
  lbSource,lbSender : TListBox;
begin
  lbSource := Source as TListBox;
  lbSender := Sender as TListbox;
  for i := 0 to lbSender.count-1 do begin
   if lbSource.Selected [i] then begin
      lbSender.Items.Add(lbSource.Items[i(*lbSource.ItemIndex*)]);
      lbSource.Items.Delete (lbSender.ItemIndex);
    end;
  end;
end;
Irgendwo ist da mindestens ein Fehler. :shock: 1. kann ich nicht über das i gehen, da die beiden Listboxen unterschiedlich viele Elemente haben können.
Delphi-Quellcode:
lbSource.Items.Delete (lbSender.ItemIndex);
kann wohl nicht gehen, sofern lbSource weniger Elemente hat als der Wert von lbSender.ItemIndex. Mir fällt leider nichts ein, was ich da machen könnte. Auch wenn ich 2 oder mehr Zeilen in der einen Listbox markiert habe und sie in die andere ziehen will, wird immer nur eine übernommen. Und noch eine Kleinigkeit: markieren kann ich nur mit SHIFT+Pfeil. Mit der Maus geht das nicht.

Der modifizierte Code für zwei Stringgrids sieht nun so aus:

Delphi-Quellcode:
unit StrTestUnit;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, Grids, StdCtrls;

type

  TStringGridExt = class(TStringGrid)
  protected
    procedure InsertRow(ARow: Longint);
    procedure DeleteRow(ARow: Longint);
  end;

  TForm2 = class(TForm)
    StringGrid1: TStringGrid;
    Button1: TButton;
    Button2: TButton;
    StringGrid2: TStringGrid;
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
    procedure StringGrid1DragOver(Sender, Source: TObject; X, Y: Integer;
      State: TDragState; var Accept: Boolean);
    procedure StringGrid1DragDrop(Sender, Source: TObject; X, Y: Integer);
    procedure StringGrid2DragDrop(Sender, Source: TObject; X, Y: Integer);
    procedure StringGrid2DragOver(Sender, Source: TObject; X, Y: Integer;
      State: TDragState; var Accept: Boolean);
  private
    {...}
  public
    {...}
  end;

var
  Form2: TForm2;

implementation

{$R *.DFM}

procedure TStringGridExt.InsertRow(ARow: Longint);
var
  TempRow: Integer;
begin
  TempRow := Row;            // Zeile zwischenspeichern
  while ARow < FixedRows do
    ARow := ARow + 1;
  RowCount := RowCount + 1;
  MoveRow(RowCount - 1, ARow);
  Row := TempRow;
  Rows[Row].Clear;
end;

procedure TStringGridExt.DeleteRow(ARow: Longint);
var
  GemRow: Integer;
begin
  GemRow := Row;
  if RowCount > FixedRows + 1 then
    inherited DeleteRow(ARow)
  else
    Rows[ARow].Clear;
  if GemRow < RowCount then Row := GemRow;
end;

procedure TForm2.Button1Click(Sender: TObject);
begin
  TStringGridExt(StringGrid1).InsertRow(1);
end;

procedure TForm2.Button2Click(Sender: TObject);
begin
  TStringGridExt(StringGrid1).DeleteRow(1);
end;

procedure TForm2.StringGrid1DragOver(Sender, Source: TObject; X, Y: Integer;
  State: TDragState; var Accept: Boolean);
begin
  Accept := true; //Source is TStringGridExt;
end;

procedure TForm2.StringGrid1DragDrop(Sender, Source: TObject; X,
  Y: Integer);
var
  lbSource,lbSender : TStringGridExt;
begin
  lbSource := Source as TStringGridExt; // FEHLER BEI TYPUMWANDLUNG
  lbSender := Sender as TStringGridExt;
  lbSender.InsertRow (lbSender.Row);
  lbSource.DeleteRow (lbSource.Row);
end;

procedure TForm2.StringGrid2DragOver(Sender, Source: TObject; X, Y: Integer;
  State: TDragState; var Accept: Boolean);
begin
  Accept := true; //Source is TStringGridExt;
end;

procedure TForm2.StringGrid2DragDrop(Sender, Source: TObject; X,
  Y: Integer);
var
  lbSource,lbSender : TStringGridExt;
begin
  lbSource := Source as TStringGridExt;
  lbSender := Sender as TStringGridExt;
  lbsender.InsertRow (lbSender.Row);
  lbSource.DeleteRow (lbSource.Row);
end;

end.
Allerdings wird er nicht compiliert. Wie oben zu sehen ist kommt "Fehler bei Typumwandlung". Ansonsten ist der Code ob ListBox oder Stringgrid fast identisch. In beiden Fällen habe ich das MouseDown allerdings eliminiert. Damit das funktioniert muß Dragmode im OI von dmManual auf dmAutomatic umgestellt werden.

Keldorn 14. Feb 2004 09:45

Re: Drag and drop
 
Liste der Anhänge anzeigen (Anzahl: 1)
Hallo Hansa

da ist ein Fehler drin (for i := 0 to lbSender.count-1 do begin - muß lbsource sein! ) und der schleifenaufbau mit einer for-schleife vorwärts führt dich eh in eine exception, rest sthet im Quelltext

Delphi-Quellcode:
procedure TForm1.ListBoxDragDrop(Sender, Source: TObject; X, Y: Integer);
//Procedure einmal in einer Listbox erstellt und umbenannt (1 entfernt)
var i,i2 : integer;
    lbSource,lbSender : TListBox;
begin
  lbSource := Source as TListBox;
  lbSender := Sender as TListbox;
  //Drag and drop der gleichen LB verhindern
  if lbSource <> lbSender then
    begin
      //items kopieren
      //du löschst items, deswegen müßte die for Schleife rückwärts zählen, dann würden die itmes aber spiegelverkehrt hinzugefügt
      //ich nehm da lieber eine while schleife, eine for-schleife macht dir das leben schwerer (finde ich)
      i:=0;
      while i<=lbSource.count-1 do //hier muß Source stehen, du willst von source nach Sender!
        begin
          if lbSource.Selected [i] then
            begin
              //Variante 1 = hinzufügen
              //lbSender.Items.Add(lbSource.Items[i]);

              //Variante 2 = Einfügen
              i2:=lbSender.ItemAtPos(point(x,y),false);
              lbSender.Items.insert(i2,lbSource.Items[i]);

              //Einträge in sourceListbox löschen
              lbSource.Items.Delete (i);
              continue; //inc(i) "überspringen" und mit gleichem item (wegen Löschung) weiterarbeiten
            end;
          inc(i);
        end;
    end;
end;
Zitat:

Und noch eine Kleinigkeit: markieren kann ich nur mit SHIFT+Pfeil. Mit der Maus geht das nicht.
bekommt man evtl auch hin, wenn du bei dragover Sender und source vergleichst, wenn gleich, dann ist es die gleiche listbox und kannst dann wieder mit itematpos arbeiten und die items markieren (nicht probiert, könnt mir aber vorstellen, das es so (oder so ähnlich :mrgreen: )gehen könnte)

Zitat:

Allerdings wird er nicht compiliert. Wie oben zu sehen ist kommt "Fehler bei Typumwandlung"
probier das mal so:
Delphi-Quellcode:
procedure TForm1.StringGridDragDrop(Sender, Source: TObject; X,
  Y: Integer);
Var StSource,StSender:TStringGrid;

begin
  StSource := Source as TStringGrid;
  StSender := Sender as TStringGrid;

  //Zeile einfügen
  TStringGridExt(StSender).InsertRow (StSender.Row);
  //datenkopieren
  StSender.Rows[StSender.Row].Assign(StSource.Rows[StSource.Row]);
  //zeile löschen
  TStringGridExt(StSource).DeleteRow (StSource.Row);
end;
auch hier würdest du nurt eine Zeile hinzufügen, willst du mit alle markierten arbeiten willst, kannst du mit stringgrid-selection arbeiten um rauszufinden, welche Zellen markiert sind.

Beim stringrid ist bei mir das Problem, das wenn dmautomatic gesetzt ist, ich keine selektierungen mehr machen kann. sämtliche Klicks läßt es kalt und es will sofort draggen. da hab ich moment aber auch keine richtige Idee.

ansonsten hab ich mein Probeprojekt mal hinzu gefügt

Mfg Frank

Hansa 14. Feb 2004 13:19

Re: Drag and drop
 
Hi,

damit bin ich schon einen ganzen Schritt weiter. :thuimb: Ich benutze aber nur noch das StringGrid. Die Listbox ist doch zu schwach. Jetzt sind aber noch ein paar Sachen nicht richtig. Ich kann eine Zeile in einen Bereich eines Grids ziehen, wo nichts ist. Dann kommt Exception. Ich vermute, daß das mit dem RowCount zu tun hat, oder irgendwo wird noch falsch gezählt.

Mehrere Zeilen zu markieren und zu verschieben ist mir auch noch nicht gelungen. 8)

Keldorn 14. Feb 2004 15:17

Re: Drag and drop
 
Zitat:

Ich kann eine Zeile in einen Bereich eines Grids ziehen, wo nichts ist. Dann kommt Exception.
warn fehler im enddrag, arow-1 wird nicht abgefangen, war ja auch nur ne notlösung.
habs jetzt anders gemacht, bei mir wird das nix mit dragmode=dmautomatic, da ich dann nix mehr auswählen kann. also wieder dmmanuel und im mousedown der Grids:
Delphi-Quellcode:
procedure TForm1.StringGridMouseDown(Sender: TObject;
  Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
begin
  TStringGrid(Sender).BeginDrag(false,10);
end;
und die drag-methoden (erlauben auch mehrere Zeilen):
Delphi-Quellcode:
procedure TForm1.StringGridDragOver(Sender, Source: TObject; X,
  Y: Integer; State: TDragState; var Accept: Boolean);
begin
  //Drag and drop im gleichem Stringgrid nichtakzeptieren
  accept := (Source is TStringGrid) and (source<>Sender);
end;

procedure TForm1.StringGridDragDrop(Sender, Source: TObject; X,
  Y: Integer);
Var StSource,StSender:TStringGrid;
    RowVon, RowBis,i,destrow,dummy :integer;

begin
  StSource := Source as TStringGrid;
  StSender := Sender as TStringGrid;

  //welche Zeilen sind im Source ausgewählt
  rowVon := StSource.Selection.top;
  rowBis := StSource.Selection.Bottom;

  //wohin einfügen?
  StSender.MouseToCell(x,y,dummy,destrow);

  //da immer nur eine zusammenhängende Markierung möglich ist, gehts hier mit For einfacher
  //aber rückwärts, wegen dem löschen
  for i:=rowbis downto Rowvon do
    begin
      //Zeile einfügen oder anhängen
      if destrow=-1 then
        begin
          //anhängen
          StSender.RowCount:=StSender.RowCount+1;
          //datenkopieren
          StSender.Rows[StSender.RowCount-1].Assign(StSource.Rows[i]);
        end
       else
        begin
          //einfügen
          TStringGridExt(StSender).InsertRow (destrow);
          //datenkopieren
          StSender.Rows[destrow].Assign(StSource.Rows[i]);
        end;
      //zeile löschen
      TStringGridExt(StSource).DeleteRow (i);
    end;
end;
ich füge die Zeilen an den betreffenden Stellen ein, nur am Ende anhängen is doof :)

mit diesem Code geht aber dein insertrow nicht mehr richtig. habs auch noch geändert:
Delphi-Quellcode:
procedure TStringGridExt.InsertRow(ARow: Longint);
var i:integer;
begin
  RowCount := RowCount + 1;
  for i:=RowCount-1 downto arow do
    rows[i].Assign(rows[i-1]);
end;
Mfg Frank


Alle Zeitangaben in WEZ +1. Es ist jetzt 15:04 Uhr.

Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024-2025 by Thomas Breitkreuz