Frage

In einer früheren Frage hier habe ich nach Drag n Drop im Gitterpanel gestellt.

Ziehen Sie n Drop -Steuerelemente in einem Gridpanel

Die Frage, die ich als nächstes habe, ist, dass ich seltsames Verhalten habe, wenn ich versuche, die Steuerung diagonal zu bewegen, wenn sie in der Nähe anderer Kontrollen sind. Kontrollen, die sich nicht bewegen, wechseln die Zellen. Auf und ab, seitlich ist es in Ordnung. Aber diagonale Bewegungen bewegt sich, wenn sich der bewegte Zellinhalt in derselben Zeile/Spalte mit anderen Zellen befindet, die Kontrollpersonen zu unerwarteten Verschiebungen verursachen. Ich habe versucht, die Verschiebungen immer noch zu beginnen. Es gibt eine Schließfunktion für das Gitterpanel, aber alles sperren. Es passiert, wenn sich der Tropfen auf einer leeren Zelle befindet, und sogar Zellen, die bereits Inhalte haben.

Hier ist das Testprojekt (Delphi 2010 mit exe)http://www.mediafire.com/?xmrgm7ydhygfw2r

type
  TForm1 = class(TForm)
    GridPanel1: TGridPanel;
    btn1: TButton;
    btn3: TButton;
    btn2: TButton;
    lbl1: TLabel;
    procedure FormCreate(Sender: TObject);
  private
    { Private declarations }
    procedure GridPanelDragDrop(Sender, Source: TObject; X, Y: Integer);
    procedure btnDragOver(Sender, Source: TObject; X, Y: Integer;
      State: TDragState; var Accept: Boolean);
    procedure btnDragDrop(Sender, Source: TObject; X, Y: Integer);
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure SetColumnWidths(aGridPanel: TGridPanel);
var
  i,pct: Integer;
begin
  aGridPanel.ColumnCollection.BeginUpdate;
  pct:=Round(aGridPanel.ColumnCollection.Count/100);
  for i := 0 to aGridPanel.ColumnCollection.Count - 1 do begin
    aGridPanel.ColumnCollection[i].SizeStyle := ssPercent;
    aGridPanel.ColumnCollection[i].Value     := pct;
  end;
  aGridPanel.ColumnCollection.EndUpdate;
end;

procedure SetRowWidths(aGridPanel: TGridPanel);
var
  i,pct: Integer;
begin
  aGridPanel.RowCollection.BeginUpdate;
  pct:=Round(aGridPanel.RowCollection.Count/100);
  for i := 0 to aGridPanel.RowCollection.Count - 1 do begin
    aGridPanel.RowCollection[i].SizeStyle := ssPercent;
    aGridPanel.RowCollection[i].Value     := pct;
  end;
  aGridPanel.RowCollection.EndUpdate;
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  btn1.OnDragOver := btnDragOver;
  btn2.OnDragOver := btnDragOver;
  btn3.OnDragOver := btnDragOver;
  GridPanel1.OnDragOver := btnDragOver;
  GridPanel1.OnDragDrop := GridPanelDragDrop;

  btn1.OnDragDrop := btnDragDrop;
  btn2.OnDragDrop := btnDragDrop;
  btn3.OnDragDrop := btnDragDrop;

  SetColumnWidths(GridPanel1);
  SetRowWidths(GridPanel1);
end;

procedure TForm1.btnDragOver(Sender, Source: TObject; X, Y: Integer;
  State: TDragState; var Accept: Boolean);
begin
  Accept := (Source is TButton);
end;

procedure TForm1.btnDragDrop(Sender, Source: TObject; X, Y: Integer);
var
  src_x,src_y, dest_x, dest_y: Integer;
  btnNameSrc,btnNameDest: string;
  src_ctrlindex,dest_ctrlindex:integer;
begin
  if Source IS tBUTTON then
  begin
    //GridPanel1.ColumnCollection.BeginUpdate;
    btnNameSrc := (Source as TButton).Name;
    btnNameDest := (Sender as TButton).Name;
    src_ctrlindex := GridPanel1.ControlCollection.IndexOf(Source as tbutton);
    src_x := GridPanel1.ControlCollection.Items[src_ctrlindex].Column;
    src_y := GridPanel1.ControlCollection.Items[src_ctrlindex].Row;

    dest_ctrlindex := GridPanel1.ControlCollection.IndexOf(Sender as tbutton);
    dest_x := GridPanel1.ControlCollection.Items[dest_ctrlindex].Column;
    dest_y := GridPanel1.ControlCollection.Items[dest_ctrlindex].Row;

    GridPanel1.ControlCollection[src_ctrlindex].Column := dest_x;
    GridPanel1.ControlCollection[src_ctrlindex].Row := dest_y;
    //GridPanel1.ColumnCollection.EndUpdate;

    lbl1.Caption := Format('"%s" from cell %d:%d to Cell %s=%d:%d', [btnNameSrc,src_x,src_y,btnNameDest,dest_x,dest_y]);

  end;
end;

procedure TForm1.GridPanelDragDrop(Sender, Source: TObject; X, Y: Integer);
var
  DropPoint: TPoint;
  CellRect: TRect;
  i_col, i_row, src_x,src_y, dest_x, dest_y: Integer;
  btnNameSrc,btnNameDest: string;
  src_ctrlindex:integer;
begin
  if Source is tbutton then
  begin
    btnNameSrc := (Source as TButton).Name;
    btnNameDest := '';
    src_ctrlindex := GridPanel1.ControlCollection.IndexOf(Source as tbutton);
    src_x := GridPanel1.ControlCollection.Items[src_ctrlindex].Column;
    src_y := GridPanel1.ControlCollection.Items[src_ctrlindex].Row;

    DropPoint := Point(X, Y);
    for i_col := 0 to GridPanel1.ColumnCollection.Count-1 do
      for i_row := 0 to GridPanel1.RowCollection.Count-1 do
      begin
        CellRect := GridPanel1.CellRect[i_col, i_row];
        if PtInRect(CellRect, DropPoint) then
        begin
          // Button was dropped over Cell[i_col, i_row]
          dest_x := i_col;
          dest_y := i_row;
          Break;
        end;
      end;
    lbl1.Caption := Format('"%s" from cell %d:%d to Cell %s=%d:%d', [btnNameSrc,src_x,src_y,btnNameDest,dest_x,dest_y]);

    GridPanel1.ControlCollection[src_ctrlindex].Column := dest_x;
    GridPanel1.ControlCollection[src_ctrlindex].Row := dest_y;
  end;
end;
War es hilfreich?

Lösung

Hier geht es nicht um das Ziehen, wenn die Spalte und die Zeile eines Elements die Änderung in zwei Schritten verändern. Mit Ihrem Code, zuerst die Spalte, dann die Zeile. Wenn in der Säulenänderung FI bereits eine andere Steuerung gibt, wird diese andere Kontrolle beiseite geschoben, auch wenn seine Zelle nicht die ultimative Position der Zielzelle der beweglichen Kontrolle ist.

Begin/endupdate funktioniert nicht, die Steuersammlung überprüft nie die Aktualisierungszahl. Was Sie tun können, ist, einen geschützten Hack zu verwenden, um auf die Steuerelemente zuzugreifen InternalSetLocation Methode. Diese Methode hat einen Parameter "Movexisting", den Sie "False" übergeben können.

type
  THackControlItem = class(TControlItem);

procedure TForm1.GridPanelDragDrop(Sender, Source: TObject; X, Y: Integer);
var
  [...]
begin
  if Source is tbutton then
  begin

    [...]

    lbl1.Caption := Format('"%s" from cell %d:%d to Cell %s=%d:%d', [btnNameSrc,src_x,src_y,btnNameDest,dest_x,dest_y]);

    THackControlItem(GridPanel1.ControlCollection[src_ctrlindex]).
        InternalSetLocation(dest_x, dest_y, False, False);
//    GridPanel1.ControlCollection[src_ctrlindex].Column := dest_x;
//    GridPanel1.ControlCollection[src_ctrlindex].Row := dest_y;
  end;
end;

Möglicherweise müssen Sie testen, ob die Zielzelle leer ist oder nicht, bevor Sie "InternalSetLocation" aufrufen, je nachdem, was Sie erwarten, die richtige Steuerbewegung zu sein.

Andere Tipps

Ich benutze eine ganz andere Möglichkeit, den Job zu erledigen ... Erstellen Sie eine ganze Einheit, um eine Methode hinzuzufügen ExtCtrls.TControlCollection ohne Einheit zu berühren ExtCtrls (Erster Hack) und eine solche Methode verwenden InternalSetLocation (zweiter Hack). Ich erkläre auch beide Hacks in diesem Beitrag.

Dann muss ich nur eine solche Einheit zur Implementierung hinzufügen (vor der Gridpanel -Deklaration) und die von mir erstellte Methode aufrufen ... sehr einfach zu verwenden.

So mache ich es Schritt für Schritt:

  1. Ich füge eine solche Einheit hinzu, die ich für einen solchen Job im Projekt verärgert habe (Datei hinzufügen)
  2. Ich füge zu meiner TForm -Schnittstelle hinzu, die Abschnittseinheit verwendet (oder wo ich sie brauche)
  3. Ich verwende meine Methode AddControlAtCell Anstatt von ExtCtrls.TControlCollection.AddControl

Hier ist die Einheit, die ich für einen solchen Job erstellt hatte, speichern Sie sie als unitTGridPanel_WithAddControlAtCell:

unit unitTGridPanel_WithAddControlAtCell;

interface

uses
    Controls
   ,ExtCtrls
   ;

type TGridPanel=class(ExtCtrls.TGridPanel)
   private
   public
     procedure AddControlAtCell(AControl:TControl;AColumn:Integer;ARow:Integer); // Add Control on specifed cell, if there already exists a Control it will be deleted
 end;

implementation

uses
    SysUtils
   ;

type
    THackControlItem=class(TControlItem); // To get internal access to InternalSetLocation procedure
procedure TGridPanel.AddControlAtCell(AControl:TControl;AColumn:Integer;ARow:Integer);
var
   TheControlItem:TControlItem; // To let it be added in a specified cell, since ExtCtrls.TControlCollection.AddControl contains multiply BUGs
begin // Add Control on specifed cell, if there already exists a Control it will be deleted
     if   (-1<AColumn)and(AColumn<ColumnCollection.Count) // Cell with valid Column
       and // Cell inside valid range
          (-1<ARow)and(ARow<RowCollection.Count) // Cell with valid Row
     then begin // Valid cell, must check if there is already a control
               if   (Nil<>ControlCollection.ControlItems[AColumn,ARow]) // Check if there are any controls
                 and // A control is already on the cell
                    (Nil<>ControlCollection.ControlItems[AColumn,ARow].Control) // Check if cell has a control
               then begin // There is already a control, must be deleted
                         ControlCollection.Delete(ControlCollection.IndexOf(ControlCollection.ControlItems[AColumn,ARow].Control)); // Delete the control
                    end;
               TheControlItem:=ControlCollection.Add; // Create the TControlItem
               TheControlItem.Control:=TControl(AControl); // Put the Control in the specified cell without altering any other cell
               THackControlItem(ControlCollection.Items[ControlCollection.IndexOf(AControl)]).InternalSetLocation(AColumn,ARow,False,False); // Put the ControlItem in the cell without altering any other cell
          end
     else begin // Cell is out of range
               raise Exception.CreateFmt('Cell [%d,%d] out of range on ''%s''.',[AColumn,ARow,Name]);
          end;
end;

end.

Ich hoffe, die Kommentare sind genug klar. Bitte lesen Sie sie, um zu verstehen, warum und wie ich es mache.

Wenn ich dann dem Gridpanel in einer bestimmten Zelle eine Kontrolle hinzufügen muss, mache ich den nächsten einfachen Anruf:

TheGridPanel.AddControlAtCell(TheControl,ACloumn,ARow); // Add it at desired cell without affecting other cells

Ein sehr, sehr grundlegendes Beispiel für das Hinzufügen einer neu erstellten Laufzeit in einer bestimmten Zelle könnte wie folgt sein:

// AColumn      is of Type Integer
// ARow         is of Type Integer
// ACheckBox    is of Type TCheckBox
// TheGridPanel is of Type TGridPanel
ACheckBox:=TCheckBox.Create(TheGridPanel); // Create the Control to be added (a CheckBox)
ACheckBox.Visible:=False; // Set it to not visible, for now (optimization on speed, e tc)
ACheckBox.Color:=TheGridPanel.Color; // Just to use same background as on the gridpanel
ACheckBox.Parent:=TheGridPanel; // Set the parent of the control as the gridpanel (mandatory)
TheGridPanel.AddControlAtCell(ElCheckBox,ACloumn,ARow); // Add it at desired cell without affecting other cells
ElCheckBox.Visible:=True; // Now it is added, make it visible
ElCheckBox.Enabled:=True; // And of course, ensure it is enabled if needed

Bitte beachten Sie, dass ich diese beiden Hacks verwende:

  1. type THackControlItem Lassen Sie mich auf die Methode zugreifen InternalSetLocation.
  2. type TGridPanel=class(ExtCtrls.TGridPanel) Lassen Sie mich eine Methode hinzufügen ExtCtrls.TGridPanel ohne auch auch nur zu berühren (auch keine Quelle von ExtCtrls)

Wichtig: Beachten Sie auch, dass ich es erwähne, das Gerät zur Verwendung der Schnittstelle jedes Formulars hinzuzufügen, in dem Sie die Methode verwenden möchten AddControlAtCell; Das gilt für normale Personen, fortgeschrittene Personen könnten auch eine andere Einheit erstellen usw. Das „Konzept“ besteht darin Putted zur Entwurfszeit in einem Formular ... es muss die Implementierung der Verwendung einer solchen Formulareinheit anwenden.

Hoffe das hilft jemand anderem.

Die folgende Lösung funktioniert ohne jegliche Art von Hacking.

Mein Code ist im C ++ - Builder, aber ich denke, er ist einfach für Delphi -Benutzer zu verstehen, da er nur auf VCL -Funktionen beruht. PS: Beachten Sie, dass ich TPanels anstelle von Tbuttons ziehe (eine sehr geringfügige Änderung).

void TfrmVCL::ButtonDragDrop(TObject *Sender, TObject *Source, int X, int Y)
{
  TRect CurCellRect;
  TRect DestCellRect;
  int Col;
  int Row;
  int destCol; int destRow;
  int srcIndex; int destIndex;
  TPanel *SrcBtn;
  TPanel *DestBtn;

  SrcBtn = dynamic_cast<TPanel *>(Source);
  if (SrcBtn)
     {
     int ColCount = GridPnl->ColumnCollection->Count ;
     int RowCount = GridPnl->RowCollection->Count ;

     // SOURCE
     srcIndex = GridPnl->ControlCollection->IndexOf( SrcBtn );

     // DESTINATION
     // we get coordinates of the button I drag onto
     DestBtn= dynamic_cast<TPanel *>(Sender);
     if (!DestBtn) return;
     destIndex    = GridPnl->ControlCollection->IndexOf( DestBtn );
     destCol      = GridPnl->ControlCollection->Items[ destIndex ]->Column;  // the column for the dragged button
     destRow      = GridPnl->ControlCollection->Items[ destIndex ]->Row;
     DestCellRect = GridPnl->CellRect[ destCol ][ destRow ];

     // Check all cells
     for ( Col = 0 ; Col < ColCount ; Col++ )
        {
        for ( Row = 0 ; Row < RowCount ; Row++ )
           {
             // Get the bounding rect for this cell
             CurCellRect = GridPnl->CellRect[ Col ][ Row ];

             if (IntersectRect_ForReal(DestCellRect, CurCellRect))
                {
                GridPnl->ControlCollection->Items[srcIndex]->SetLocation(Col, Row, false);
                return;
                }
           }
        }
     }
}
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top