4

ここでの前の質問で、グリッドパネル内のドラッグアンドドロップについて質問しました。

GridPanelでNドロップコントロールをドラッグ

次に質問するのは、コントロールが他のコントロールの近くにあるときに、コントロールを斜めに動かそうとすると、奇妙な動作をするということです。移動することを想定していないコントロールは、セルをシフトしています。上下、横向きでも大丈夫です。ただし、移動したセルの内容がコントロールを保持している他のセルと同じ行/列にある場合、斜めに移動すると予期しないシフトが発生します。beginupdate / endupdateを試しましたが、シフトはまだ発生します。グリッドパネルにはLOCK機能がありますが、何でもロックします。これは、ドロップが空のセル、およびすでにコンテンツを持っているセルにある場合に発生します。

これがテストプロジェクトです(Delphi 2010 w / o 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;
4

3 に答える 3

4

これはドラッグではありません。アイテムの列と行の両方が変更されている場合、変更は2つのステップで発生します。コードでは、最初に列、次に行です。列の変更fiで、他のコントロールがすでに存在する場合、そのセルが移動コントロールのターゲットセルの最終的な位置でなくても、この他のコントロールは押しのけられます。

Begin / EndUpdateは機能せず、コントロールコレクションは更新カウントをチェックしません。何ができるかは、保護されたハックを使用してコントロールアイテムのInternalSetLocationメソッドにアクセスすることです。このメソッドには、「False」を渡すことができる「MoveExisting」パラメーターがあります。

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;

正しいコントロールの動きと予想されるものによっては、「InternalSetLocation」を呼び出す前に、ターゲットセルが空かどうかをテストする必要がある場合があります。

于 2011-03-20T16:40:52.460 に答える
1

私は仕事をするためにまったく異なる方法を使用しています...ユニットにExtCtrls.TControlCollection触れずにメソッドを追加するためだけにユニット全体を作成しExtCtrls(最初のハック)、そのようなメソッドを使用します InternalSetLocation(2番目のハック)。また、この投稿で両方のハックについて説明します。

次に、そのようなユニットを実装使用セクション(gridpanel宣言の前)に追加し、作成したメソッドを呼び出すだけです...非常に簡単に使用できます。

これが私がそれを行う方法です、ステップバイステップ:

  1. そのような仕事のために作ったそのようなユニットをプロジェクトに含めます(ファイルを追加します)
  2. TFormインターフェイスの使用セクションにそのようなユニット(または必要な場所)を追加します
  3. AddControlAtCell代わりに自分の方法を使用しますExtCtrls.TControlCollection.AddControl

これが私がそのような仕事のために作成したユニットです、それを次のように保存して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.

コメントが十分に明確であることを願っています。私がそれを行う理由と方法を理解するためにそれらを読んでください。

次に、指定したセルのグリッドパネルにコントロールを追加する必要がある場合は、次の簡単な呼び出しを行います。

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

特定のセルに新しく作成されたランタイムTCheckBoxを追加する非常に基本的な例は、次のようになります。

// 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

私はこの2つのハックを使用していることに注意してください:

  1. type THackControlItemメソッドにアクセスさせてくださいInternalSetLocation
  2. type TGridPanel=class(ExtCtrls.TGridPanel)触れずにメソッドを追加させてくださいExtCtrls.TGridPanel(ソースも必要ありませんExtCtrls

重要:メソッドを使用する各フォームのインターフェイスの使用にユニットを追加する必要があることにも注意してくださいAddControlAtCell。つまり、普通の人、上級者は別のユニットを作成することもできます...「コンセプト」は、使用したいGridPanelの宣言の前にユニットを使用することです...例:GridPanelが設計時にフォームに配置されます...そのようなフォームユニットの実装使用を継続する必要があります。

これが他の誰かに役立つことを願っています。

于 2013-07-09T09:06:04.660 に答える
0

以下の解決策は、ハッキングなしで機能します。

私のコードはC++Builderにありますが、VCL関数のみに依存しているため、Delphiユーザーにとっては理解しやすいと思います。PS:TButtonの代わりにTPanelsをドラッグすることに注意してください(非常に小さな変更です)。

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;
                }
           }
        }
     }
}
于 2018-11-21T14:51:06.397 に答える