问题
How can I allow selected rows in a DataGridView (DGV) to be moved up or down. I have done this before with a ListView. Unfortunetly, for me, replacing the DGV is not an option (curses). By the way, the DGV datasource is a Generic Collection.
The DGV has two buttons on the side, yes, UP & Down. Can anyone help point me in the right direction. I do have the code that I used for the ListView if it'll help (it did not help me).
回答1:
If you programatically change the ordering of the items in your collection, the DGV should reflect that automatically.
Sloppy, half-working example:
List<MyObj> foo = DGV.DataSource;
int idx = DGV.SelectedRows[0].Index;
int value = foo[idx];
foo.Remove(value);
foo.InsertAt(idx+1, value)
Some of that logic may be wrong, and this may not be the most efficient approach either. Also, it doesn't take into account multiple row selections.
Hmm, one last thing, if you're using a standard List or Collection this isn't going to go as smoothly. List and Collection on't emit events that the DGV finds useful for databinding. You could 'burp' the databinding every time you change the collection, but a better solution would be for you to use a System.ComponentModel.BindingList. When you change the ordering of the BindingList the DGV should reflect the change automatically.
回答2:
Just to expand on Yoopergeek's answer, here's what I have. I was not using a DataSource (data is being dropped to registry on form close, and reload on form load)
This sample will keep rows from being moved off the grid and lost, and reselect the cell the person was in as well.
To make things simpler for copy / paste, I modified so you need only change "gridTasks" to your DataGridView's name, rather than renaming it throughout the code.
This solution works only for single cell/row selected.
private void btnUp_Click(object sender, EventArgs e)
{
DataGridView dgv = gridTasks;
try
{
int totalRows = dgv.Rows.Count;
// get index of the row for the selected cell
int rowIndex = dgv.SelectedCells[ 0 ].OwningRow.Index;
if ( rowIndex == 0 )
return;
// get index of the column for the selected cell
int colIndex = dgv.SelectedCells[ 0 ].OwningColumn.Index;
DataGridViewRow selectedRow = dgv.Rows[ rowIndex ];
dgv.Rows.Remove( selectedRow );
dgv.Rows.Insert( rowIndex - 1, selectedRow );
dgv.ClearSelection();
dgv.Rows[ rowIndex - 1 ].Cells[ colIndex ].Selected = true;
}
catch { }
}
private void btnDown_Click(object sender, EventArgs e)
{
DataGridView dgv = gridTasks;
try
{
int totalRows = dgv.Rows.Count;
// get index of the row for the selected cell
int rowIndex = dgv.SelectedCells[ 0 ].OwningRow.Index;
if ( rowIndex == totalRows - 1 )
return;
// get index of the column for the selected cell
int colIndex = dgv.SelectedCells[ 0 ].OwningColumn.Index;
DataGridViewRow selectedRow = dgv.Rows[ rowIndex ];
dgv.Rows.Remove( selectedRow );
dgv.Rows.Insert( rowIndex + 1, selectedRow );
dgv.ClearSelection();
dgv.Rows[ rowIndex + 1 ].Cells[ colIndex ].Selected = true;
}
catch { }
}
回答3:
This should work. I use a BindingSource instead of binding my List directly to the DataGridView:
private List<MyItem> items = new List<MyItem> {
new MyItem {Id = 0, Name = "Hello"},
new MyItem {Id = 1, Name = "World"},
new MyItem {Id = 2, Name = "Foo"},
new MyItem {Id = 3, Name = "Bar"},
new MyItem {Id = 4, Name = "Scott"},
new MyItem {Id = 5, Name = "Tiger"},
};
private BindingSource bs;
private void Form1_Load(object sender, EventArgs e)
{
bs = new BindingSource(items, string.Empty);
dataGridView1.DataSource = bs;
}
private void button1_Click(object sender, EventArgs e)
{
if (bs.Count <= 1) return; // one or zero elements
int position = bs.Position;
if (position <= 0) return; // already at top
bs.RaiseListChangedEvents = false;
MyItem current = (MyItem)bs.Current;
bs.Remove(current);
position--;
bs.Insert(position, current);
bs.Position = position;
bs.RaiseListChangedEvents = true;
bs.ResetBindings(false);
}
private void button2_Click(object sender, EventArgs e)
{
if (bs.Count <= 1) return; // one or zero elements
int position = bs.Position;
if (position == bs.Count - 1) return; // already at bottom
bs.RaiseListChangedEvents = false;
MyItem current = (MyItem)bs.Current;
bs.Remove(current);
position++;
bs.Insert(position, current);
bs.Position = position;
bs.RaiseListChangedEvents = true;
bs.ResetBindings(false);
}
public class MyItem
{
public int Id { get; set; }
public String Name { get; set; }
}
回答4:
First fill your datagridview,for example you got table with 3 colums
DataTable table = new DataTable();
table.Columns.Add("col1");
table.Columns.Add("col2");
table.Columns.Add("col3");
foreach (var i in yourTablesource(db,list,etc))
{
table.Rows.Add(i.col1, i.col2, i.col2);
}
datagridview1.DataSource = table;
Then, on button up click
int rowIndex;
private void btnUp_Click(object sender, EventArgs e)
{
rowIndex = datagridview1.SelectedCells[0].OwningRow.Index;
DataRow row = table.NewRow();
row[0] = datagridview1.Rows[rowIndex].Cells[0].Value.ToString();
row[1] = datagridview1.Rows[rowIndex].Cells[1].Value.ToString();
row[2] = datagridview1.Rows[rowIndex].Cells[2].Value.ToString();
if (rowIndex > 0)
{
table.Rows.RemoveAt(rowIndex);
table.Rows.InsertAt(row, rowIndex - 1);
datagridview1.ClearSelection();
datagridview1.Rows[rowIndex - 1].Selected = true;
}
}
Do the same thing for button down, just change row index from rowIndex - 1 to rowindex + 1 in your buttonDown_Click method
回答5:
There is a much simpler way that most posts here (in my opinion). Performing the action for an "up" button click is basically just a swap of rows with the one above. If you are controlling the values yourself (as the question was stated) then you just need to swap the values of the rows. Quick and simple!
NOTE: this works only when multiselect is disabled on the datagrid! As you can tell I am only paying attention to item at index 0 in the SelectedRows collection.
Here is what I used:
private void btnUp_Click(object sender, EventArgs e)
{
var row = dgvExportLocations.SelectedRows[0];
if (row != null && row.Index > 0)
{
var swapRow = dgvExportLocations.Rows[row.Index - 1];
object[] values = new object[swapRow.Cells.Count];
foreach (DataGridViewCell cell in swapRow.Cells)
{
values[cell.ColumnIndex] = cell.Value;
cell.Value = row.Cells[cell.ColumnIndex].Value;
}
foreach (DataGridViewCell cell in row.Cells)
cell.Value = values[cell.ColumnIndex];
dgvExportLocations.Rows[row.Index - 1].Selected = true;//have the selection follow the moving cell
}
}
To perform a "down" click you can do the opposite as well, same logic
回答6:
Was looking for this UP/DOWN button thing and glad that I found this. Better to put the bs.RaiseListChangedEvents = false statement after the return or it doesn't work all the time.
And in C#3.0 you can add two extension methods to the BindingSource like this:
public static class BindingSourceExtension
{
public static void MoveUp( this BindingSource aBindingSource )
{
int position = aBindingSource.Position;
if (position == 0) return; // already at top
aBindingSource.RaiseListChangedEvents = false;
object current = aBindingSource.Current;
aBindingSource.Remove(current);
position--;
aBindingSource.Insert(position, current);
aBindingSource.Position = position;
aBindingSource.RaiseListChangedEvents = true;
aBindingSource.ResetBindings(false);
}
public static void MoveDown( this BindingSource aBindingSource )
{
int position = aBindingSource.Position;
if (position == aBindingSource.Count - 1) return; // already at bottom
aBindingSource.RaiseListChangedEvents = false;
object current = aBindingSource.Current;
aBindingSource.Remove(current);
position++;
aBindingSource.Insert(position, current);
aBindingSource.Position = position;
aBindingSource.RaiseListChangedEvents = true;
aBindingSource.ResetBindings(false);
}
}
Finally a good use for extension methods instead of all those bad String examples.. ;-)
回答7:
DataGridViewRow BeginingRow = new DataGridViewRow();
int BeginingRowIndex ;
private void DataGridView1_CellMouseUp(object sender, DataGridViewCellMouseEventArgs e)
{
if (e.Button != MouseButtons.Left ||e.RowIndex < 0 ) return;
if (BeginingRowIndex > e.RowIndex)
{
DataGridView1.Rows.Insert(e.RowIndex);
foreach (DataGridViewCell cellules in BeginingRow.Cells)
{
DataGridView1.Rows[e.RowIndex].Cells[cellules.ColumnIndex].Value = cellules.Value;
}
DataGridView1.Rows.RemoveAt(BeginingRowIndex + 1);
}
else
{
DataGridView1.Rows.Insert(e.RowIndex +1);
foreach (DataGridViewCell cellules in BeginingRow.Cells)
{
DataGridView1.Rows[e.RowIndex+1].Cells[cellules.ColumnIndex].Value = cellules.Value;
}
DataGridView1.Rows.RemoveAt(BeginingRowIndex);
}
DataGridView1.RowsDefaultCellStyle.ApplyStyle(BeginingRow.DefaultCellStyle);
DataGridView1.Rows[e.RowIndex].Selected = true;
}
private void DataGridView1_CellMouseDown(object sender, DataGridViewCellMouseEventArgs e)
{
if (e.Button != MouseButtons.Left ||e.RowIndex < 0 ) return;
BeginingRowIndex = e.RowIndex;
BeginingRow = DataGridView1.Rows[BeginingRowIndex];
BeginingRow.DefaultCellStyle = DataGridView1.Rows[BeginingRowIndex].DefaultCellStyle;
}
回答8:
private void butUp_Click(object sender, EventArgs e)
{
DataTable dtTemp = gridView.DataSource as DataTable;
object[] arr = dtTemp.Rows[0].ItemArray;
for (int i = 1; i < dtTemp.Rows.Count; i++)
{
dtTemp.Rows[i - 1].ItemArray = dtTemp.Rows[i].ItemArray;
}
dtTemp.Rows[dtTemp.Rows.Count - 1].ItemArray = arr;
}
private void butDown_Click(object sender, EventArgs e)
{
DataTable dtTemp = gridView.DataSource as DataTable;
object[] arr = dtTemp.Rows[dtTemp.Rows.Count - 1].ItemArray;
for (int i = dtTemp.Rows.Count - 2; i >= 0; i--)
{
dtTemp.Rows[i + 1].ItemArray = dtTemp.Rows[i].ItemArray;
}
dtTemp.Rows[0].ItemArray = arr;
}
回答9:
this is the shortest solution I have found to the problem and I just refactored a little bit the code found in:
http://dotnetspeaks.net/post/Moving-GridView-Rows-Up-Down-in-a-GridView-Control.aspx
<body>
<form id="form1" runat="server">
<asp:GridView ID="GridView1" Font-Names="Verdana" Font-Size="9pt" runat="server" OnRowCreated="GridView1_RowCreated"
AutoGenerateColumns="False" CellPadding="4" BorderColor="#507CD1" BorderStyle="Solid">
<Columns>
<asp:TemplateField HeaderText="First Name">
<ItemTemplate>
<asp:Label ID="txtFirstName" runat="server" Text='<%# Eval("FirstName") %>' />
</ItemTemplate>
</asp:TemplateField>
</Columns>
<HeaderStyle BackColor="#507CD1" Font-Bold="True" ForeColor="White" />
<AlternatingRowStyle BackColor="White" />
</asp:GridView>
<asp:Button ID="btnUp" runat="server" Text="Up" OnClick="btnUp_Click"/>
<asp:Button ID="btnDown" runat="server" Text="Down" OnClick="btnDown_Click" />
</form>
and with code behind...
public int SelectedRowIndex { get; set; }
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
{
//Test Records
GridView1.DataSource = Enumerable.Range(1, 5).Select(a => new
{
FirstName = String.Format("First Name {0}", a),
LastName = String.Format("Last Name {0}", a),
});
GridView1.DataBind();
}
}
protected void GridView1_RowCreated(object sender, GridViewRowEventArgs e)
{
if (e.Row.RowType == DataControlRowType.DataRow)
{
e.Row.Attributes["onmouseover"] = "this.style.cursor='pointer'";
e.Row.ToolTip = "Click to select row";
e.Row.Attributes["onclick"] = Page.ClientScript.GetPostBackClientHyperlink(GridView1, "Select$" + e.Row.RowIndex);
}
}
protected void btnUp_Click(object sender, EventArgs e)
{
var rows = GridView1.Rows.Cast<GridViewRow>().Where(a => a != GridView1.SelectedRow).ToList();
//If First Item, insert at end (rotating positions)
if (GridView1.SelectedRow.RowIndex.Equals(0))
{
rows.Add(GridView1.SelectedRow);
SelectedRowIndex = GridView1.Rows.Count -1;
}
else
{
SelectedRowIndex = GridView1.SelectedRow.RowIndex - 1;
rows.Insert(GridView1.SelectedRow.RowIndex - 1, GridView1.SelectedRow);
}
RebindGrid(rows);
}
protected void btnDown_Click(object sender, EventArgs e)
{
var rows = GridView1.Rows.Cast<GridViewRow>().Where(a => a != GridView1.SelectedRow).ToList();
//If Last Item, insert at beginning (rotating positions)
if (GridView1.SelectedRow.RowIndex.Equals(GridView1.Rows.Count - 1))
{
rows.Insert(0, GridView1.SelectedRow);
SelectedRowIndex = 0;
}
else
{
SelectedRowIndex = GridView1.SelectedRow.RowIndex + 1;
rows.Insert(GridView1.SelectedRow.RowIndex + 1, GridView1.SelectedRow);
}
RebindGrid(rows);
}
private void RebindGrid(IEnumerable<GridViewRow> rows)
{
GridView1.DataSource = rows.Select(a => new
{
FirstName = ((Label)a.FindControl("txtFirstName")).Text,
}).ToList();
GridView1.SelectedIndex = SelectedRowIndex;
GridView1.DataBind();
}
回答10:
Header 3
private void buttonX8_Click(object sender, EventArgs e)//down { DataGridViewX grid = dataGridViewX1; try { int totalRows = grid.Rows.Count; int idx = grid.SelectedCells[0].OwningRow.Index; if (idx == totalRows - 1 ) return; int col = grid.SelectedCells[0].OwningColumn.Index; DataGridViewRowCollection rows = grid.Rows; DataGridViewRow row = rows[idx]; rows.Remove(row); rows.Insert(idx + 1, row); grid.ClearSelection(); grid.Rows[idx + 1].Cells[col].Selected = true;
private void buttonX8_Click(object sender, EventArgs e)//down
{
DataGridViewX grid = dataGridViewX1;
try
{
int totalRows = grid.Rows.Count;
int idx = grid.SelectedCells[0].OwningRow.Index;
if (idx == totalRows - 1 )
return;
int col = grid.SelectedCells[0].OwningColumn.Index;
DataGridViewRowCollection rows = grid.Rows;
DataGridViewRow row = rows[idx];
rows.Remove(row);
rows.Insert(idx + 1, row);
grid.ClearSelection();
grid.Rows[idx + 1].Cells[col].Selected = true;
}
catch { }
}
回答11:
SchlaWiener's answer worked well, and I just wanna add something to it:
private void button1_Click(object sender, EventArgs e) //The button to move up
{
int position = bs.Position;
//.......neglected.......
dataGridView1.ClearSelection();
dataGridView1.Rows[position].Selected = true;
bs.MovePrevious();
}
Adding those 3 lines at the bottom to also make the selection move (both bindingSource and dataGridView), so that we can continuously click the bottom to move a row up.
For moving down just call bs.MoveNext()
(I have not enough reputation to post as comment yet)
回答12:
data bound solution with multi-selection support, use SharpDevelop 4.4 to convert to C#.
<Extension()>
Sub MoveSelectionUp(dgv As DataGridView)
If dgv.CurrentCell Is Nothing Then Exit Sub
dgv.CurrentCell.OwningRow.Selected = True
Dim items = DirectCast(dgv.DataSource, BindingSource).List
Dim selectedIndices = dgv.SelectedRows.Cast(Of DataGridViewRow).Select(Function(row) row.Index).Sort
Dim indexAbove = selectedIndices(0) - 1
If indexAbove = -1 Then Exit Sub
Dim itemAbove = items(indexAbove)
items.RemoveAt(indexAbove)
Dim indexLastItem = selectedIndices(selectedIndices.Count - 1)
If indexLastItem = items.Count Then
items.Add(itemAbove)
Else
items.Insert(indexLastItem + 1, itemAbove)
End If
End Sub
<Extension()>
Sub MoveSelectionDown(dgv As DataGridView)
If dgv.CurrentCell Is Nothing Then Exit Sub
dgv.CurrentCell.OwningRow.Selected = True
Dim items = DirectCast(dgv.DataSource, BindingSource).List
Dim selectedIndices = dgv.SelectedRows.Cast(Of DataGridViewRow).Select(Function(row) row.Index).Sort
Dim indexBelow = selectedIndices(selectedIndices.Count - 1) + 1
If indexBelow >= items.Count Then Exit Sub
Dim itemBelow = items(indexBelow)
items.RemoveAt(indexBelow)
Dim indexAbove = selectedIndices(0) - 1
items.Insert(indexAbove + 1, itemBelow)
End Sub
回答13:
Try this:
private void buttonX9_Click(object sender, EventArgs e)//up
{
DataGridViewX grid = dataGridViewX1;
try
{
int totalRows = grid.Rows.Count;
int idx = grid.SelectedCells[0].OwningRow.Index;
if (idx == 0)
return;
int col = grid.SelectedCells[0].OwningColumn.Index;
DataGridViewRowCollection rows = grid.Rows;
DataGridViewRow row = rows[idx];
rows.Remove(row);
rows.Insert(idx - 1, row);
grid.ClearSelection();
grid.Rows[idx - 1].Cells[col].Selected = true;
}
catch { }
}
来源:https://stackoverflow.com/questions/1012708/datagridview-selected-row-move-up-and-down