I need to implement some kind of accordion effect in a Windows Form DataGridView. When the user selects a row, the row is expanded to display some more information and if po
This is not really hard to do. The best approach would be to create a dedicated UserControl
and overlay it at the right spot.
To make room for it simply change the Row's Height
, keeping track of the row, so you can revert it when it looses selection.
You will also have to decide if more than one row can be expanded and just what the user is supposed to do to expand and to reset the row..
The UserControl
could have a function displayRowData(DataGridViewRow row)
which you could call to display the fields you are interested in in its Labels
etc..
You should also have a plan on how the Buttons
shall interact with the DataGridView
..
If you only want one Row to be expanded at a time, you can create the UC
up front, make the DGV
its Parent
and hide it. Later upon the user interaction, like clicking at the row, or a certain cell you would move it to the right row and show it..
If more than one row can be expanded you will need to create several UCs
and keep track of them in a List
..
Here is a minimal example to encourage you..:
int normalRowHeight = -1;
UcRowDisplay display = new UcRowDisplay();
DataGridViewRow selectedRow = null;
public Form1()
{
InitializeComponent();
// create one display object
display = new UcRowDisplay();
display.Visible = false;
display.Parent = DGV;
display.button1Action = someAction;
}
After you have filled the DGV
// store the normal row height
normalRowHeight = DGV.Rows[0].Height;
You need at least these events:
private void DGV_SelectionChanged(object sender, EventArgs e)
{
if (selectedRow != null) selectedRow.Height = normalRowHeight;
if (DGV.SelectedRows.Count <= 0)
{
selectedRow = null;
display.Hide();
return;
}
// assuming multiselect = false
selectedRow = DGV.SelectedRows[0];
// assuming ColumnHeader show with the same height as the rows
int y = (selectedRow.Index + 1) * normalRowHeight;
display.Location = new Point(1, y);
// filling out the whole width of the DGV.
// maybe you need more, if the DGV is scrolling horizontally
// or less if you show a vertical scrollbar..
display.Width = DGV.ClientSize.Width;
// make room for the display object
selectedRow.Height = display.Height;
// tell it to display our row data
display.displayRowData(selectedRow);
// show the display
display.Show();
}
private void DGV_ColumnWidthChanged(object sender, DataGridViewColumnEventArgs e)
{
// enforce refresh on the display
display.Refresh();
}
Here is a test action triggered by the button in the display object:
public void someAction(DataGridViewRow row)
{
Console.WriteLine(row.Index + " " + row.Cells[2].Value.ToString());
}
And of course you need a UserControl. Here is a simple one, with two Labels, one Button and one extra Label to close the display:
public partial class UcRowDisplay : UserControl
{
public UcRowDisplay()
{
InitializeComponent();
}
public delegate void someActionDelegate(DataGridViewRow row);
public someActionDelegate button1Action { get; set; }
DataGridViewRow myRow = null;
public void displayRowData(DataGridViewRow row)
{
myRow = row;
label1.Text = row.Cells[1].Value.ToString();
label2.Text = row.Cells[0].Value.ToString();
rowDisplayBtn1.Text = row.Cells[2].Value.ToString();
}
private void rowDisplayBtn1_Click(object sender, EventArgs e)
{
button1Action(myRow);
}
private void label_X_Click(object sender, EventArgs e)
{
myRow.Selected = false;
this.Hide();
}
}
It contains tthree Labels
and a Button
. In the Designer it looks like this:
Note that in order to make it a little easier on me I have modified the DGV to
Update
Here is a quick example for scrolling:
private void DGV_Scroll(object sender, ScrollEventArgs e)
{
DGV.PerformLayout();
var ccr = DGV.GetCellDisplayRectangle(0, selectedRow.Index, true);
display.Top = ccr.Top + normalRowHeight; // **
display.Visible = (ccr.Top >= 0 && ccr.Height > 0);
}
This (**) assumes the that the selected row shall remain visible. The same calculation should happen in the SelectionChanged event! It is up to you to decide what should happen when horizontal scrolling occurs..
Note that similar updates are necessary when rows are added or removed before the current row. So it should be moved to a function you call on those occurrances..