Search code examples
delphic++buildervcltgridpanel

Change the order of rows in TGridPanel by button click


I like to change the order of rows in a TGridPanel by clicking a button. The Gridpanel rows are dymamically created and contain a Panel with a manually docked form. The form has its own edit components and a Label with the index of the current row. The best way would be like this:

  1. Mark the row by clicking it
  2. Click a button "up" or "down".
  3. The selected row moves up/down.
  4. The Index of the row changes in the docked form.

Here is my attempt:

__fastcall TForm1::TForm1(TComponent* Owner)
    : TForm(Owner)
{
    GridPanel1->RowCollection->Clear();
}
//---------------------------------------------------------------------------

void __fastcall TForm1::BitBtn_StepAddClick(TObject *Sender)
{
    GridPanel1->RowCollection->BeginUpdate();

    if (GridPanel1->RowCollection->Count > 0)
        GridPanel1->Height = GridPanel1->Height + 156;

    TRowItem * RowItem = GridPanel1->RowCollection->Add();
    RowItem->SizeStyle = ssAbsolute;
    RowItem->Value     = 156;

    TPanel * Panel2 = new TPanel(this);
    Panel2->Parent = GridPanel1;
    Panel2->Color  = clWhite;
    Panel2->Name   = "Panel" + IntToStr(GridPanel1->RowCollection->Count) + "2";
    Panel2->Align  = alClient;
    Panel2->AlignWithMargins = false;

    TForm2 * Form2 = new TForm2(this, GridPanel1->RowCollection->Count);
    Form2->ManualDock(Panel2, Panel2, alClient);
    Form2->Show();

    BitBtn_StepDelete->Enabled = true;

    GridPanel1->RowCollection->EndUpdate();
}
//---------------------------------------------------------------------------

void __fastcall TForm1::BitBtn_StepDeleteClick(TObject *Sender)
{
    // at the moment this button deletes the last row in GridPanel. But it would be fine to select a row by clicking it and delete the selected one.
    // After deleting it, the titles of the manually docked forms should change
    GridPanel1->RowCollection->BeginUpdate();

    GridPanel1->ControlCollection->Delete(GridPanel1->ControlCollection->Count - 1);
    GridPanel1->RowCollection->Delete(GridPanel1->RowCollection->Count - 1);

    if (GridPanel1->RowCollection->Count > 0)
        GridPanel1->Height = GridPanel1->Height - 156;
    else
        BitBtn_StepDelete->Enabled = false;

    GridPanel1->RowCollection->EndUpdate();
}
//---------------------------------------------------------------------------

void __fastcall TForm1::BitBtn_StepInsertClick(TObject *Sender)
{
    // Select a row by clicking it and then add a new row before
}
//---------------------------------------------------------------------------

void __fastcall TForm1::BitBtn_StepUpClick(TObject *Sender)
{
    // Select a row by clicking it and move it up by this button click
}
//---------------------------------------------------------------------------

void __fastcall TForm1::BitBtn_StepDownClick(TObject *Sender)
{
    // Select a row by clicking it and move it down by this button click
}
//---------------------------------------------------------------------------

Does anyone have an idea?


Solution

  • Here's a Delphi implementation of a MoveUp button click handler. It depends on a variable SelRowId: integer holding the currently selected row index, or -1 if none selected.

    It doesn't move the rows, but rather the content (e.g. panel) from one cell to another.

    procedure TForm39.MoveUpBtnClick(Sender: TObject);
    var
      NewRowId: integer;
    begin
      GridPanel1.RowCollection.BeginUpdate;
    
      if InRange(SelRowId, 1, GridPanel1.ControlCollection.Count-1) then
      begin
        NewRowId := SelRowId - 1;
        GridPanel1.ControlCollection.ControlItems[0,SelRowId].Row := NewRowId;
        SelRowId := NewRowId;
      end;
    
      GridPanel1.RowCollection.EndUpdate;
    end;
    

    The MoveDown procedure is otherwise similar, but the test of SelRowId is

    if InRange(SelRowId, 0, GridPanel1.ControlCollection.Count-2)
    

    and assignment of NewRowId is

    `NewRowId := SelRowId + 1;`
    

    SelRowId must be initialized to -1, and set to -1 whenever no panels are selected.

    I have assumed in my test app that clicking a panel ...

    // Clicking a panel
    // - deselects the panel if it was previously selected
    // - deselects any other previously selected panel in preparation to:
    // - selects the panel if it was not previously selected
    

    In my test application I get the selected panels row in two steps:

    1. Get the index of the panel via ControlCollection.IndexOf(panel)

    2. Get the row from ControlCollection[indx]

        var
          indx: integer;
          Panel: TPanel;
    
        Panel := Sender as TPanel;
        {...}
        indx := TGridPanel(Panel.Parent).ControlCollection.IndexOf(Panel);
        SelRowId := TGridPanel(Panel.Parent).ControlCollection[indx].Row;
    

    The complete implementation of my test project is here:

    implementation
    
    {$R *.dfm}
    uses Math;
    
    procedure TForm39.FormCreate(Sender: TObject);
    begin
      SelRowId := -1;
    end;
    
    procedure TForm39.MoveUpBtnClick(Sender: TObject);
    var
      NewRowId: integer;
    begin
      GridPanel1.RowCollection.BeginUpdate;
    
      if InRange(SelRowId, 1, GridPanel1.ControlCollection.Count-1) then
      begin
        NewRowId := SelRowId - 1;
        GridPanel1.ControlCollection.ControlItems[0,SelRowId].Row := NewRowId;
        SelRowId := NewRowId;
      end;
    
      GridPanel1.RowCollection.EndUpdate;
    end;
    
    procedure TForm39.MoveDnBtnClick(Sender: TObject);
    var
      NewRowId: integer;
    begin
      GridPanel1.RowCollection.BeginUpdate;
    
      if InRange(SelRowId, 0, GridPanel1.ControlCollection.Count-2) then
      begin
        NewRowId := SelRowId + 1;
        GridPanel1.ControlCollection.ControlItems[0,SelRowId].Row := NewRowId;
        SelRowId := NewRowId;
      end;
    
      GridPanel1.RowCollection.EndUpdate;
    end;
    
    procedure TForm39.AddStepBtnClick(Sender: TObject);
    var
      NewRow: TRowItem;
      P: Tpanel;
    begin
      GridPanel1.RowCollection.BeginUpdate;
    
      NewRow := GridPanel1.RowCollection.Add;
      GridPanel1.Height := GridPanel1.Height + 50;
      NewRow.SizeStyle := ssAbsolute;
      NewRow.Value := 50;
    
      P := Tpanel.Create(self);
      P.Caption := 'Panel '+IntToStr(GridPanel1.RowCollection.Count);
      P.Align := alClient;
      P.AlignWithMargins := False;
      P.Parent := GridPanel1;
      P.ParentBackground := False;
      P.ParentColor := False;
      P.Color := Random(256) shl 16 + Random(256) shl 8 + Random(256);
      P.OnClick := PanelClick;
    
      GridPanel1.RowCollection.EndUpdate;
    end;
    
    // Clicking a panel
    // - selects the panel if it was not previously selected
    // - deselects the panel if it was previously selected
    // - deselects any other previously selected panel
    procedure TForm39.PanelClick(Sender: TObject);
    var
      indx: integer;
      Panel: TPanel;
    begin
      Panel := Sender as TPanel;
    
      // previously selected panel
      if Assigned(SelPanel) then
        SelPanel.Color := SavedColor; // reset its color
      if SelPanel = Panel then        // a second click unselects
      begin
        SelPanel := nil;
        SelRowId := -1;
        Exit;
      end;
    
      SavedColor := Panel.Color;  // save newly selected panels color
      Panel.Color := clRed;       // set selected color
      SelPanel := Panel;          // set selected panel
    
      indx := TGridPanel(Panel.Parent).ControlCollection.IndexOf(Panel);
      SelRowId := TGridPanel(Panel.Parent).ControlCollection[indx].Row;
    end;