DataGridView选定行上下移动

18

如何使一个 DataGridView(DGV)中的选定行可以向上或向下移动。 我以前用 ListView 做过这个功能,但很不幸,对我来说,替换 DGV 不是一个选项 (糟糕)。 顺便说一句,DGV 数据源是泛型集合。

DGV 有两个侧面的按钮,是的,向上和向下。 有人能帮我指点一下方向吗?如果有必要,我可以提供我用于 ListView 的代码,但它没有帮助到我。

14个回答

42

为了扩展Yoopergeek的答案,这里是我的解决方法。 我没有使用DataSource(数据在表单关闭时被放入注册表中,并在表单加载时重新加载)。

此示例可以防止行从网格上移动并丢失,并重新选择人员所在的单元格。

为了简化复制/粘贴操作,我修改了代码,只需要更改“gridTasks”为您的DataGridView名称,而不是在整个代码中重命名它。

此解决方案仅适用于单个单元格/行选定的情况。

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 { }
}

2
这个答案比被选中的答案好多了。 - user1200540
3
也许我的DGV有些问题,在向下移动时我不得不将 (idx == totalRows - 2) 更改为 (idx == totalRows - 1),否则它会允许底行被移出,而倒数第二行无法向下移动。更改后,我的程序运行完美。 - Kevin Denham
这个完美无缺,谢谢。这是我使用的C++版本:http://pastebin.com/DwYTRSiy - Oneiros
如果DGV绑定到数据库源,可能会发生异常! - Ahmed Suror

12

这应该可行。我使用BindingSource而不是将List直接绑定到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; }
    }

BindingSource的货币处理比使用BindingList<T>更加优越。 - Yoopergeek
我也喜欢这个。我会记住下次再用。谢谢你的帮助! - Silly Rabbit
我在示例代码中添加了一些行。如果您使用大型列表,可以将RaiseListChangedEvents设置为false(这可以防止datagridview在删除或添加项目后重新绘制自身)。 - Jürgen Steinblock
这很棒!但是有一些bug: 如果(position <= 0),则返回;//已经在顶部 如果(position == bs.Count - 1 || bs.Count-1<0),则返回;//已经在底部 因为如果为空,会抛出异常。 - DiSaSteR

8
如果您以编程方式更改集合中项目的排序,DGV应自动反映这一点。
不好的、半工作的示例:
List<MyObj> foo = DGV.DataSource;
int idx = DGV.SelectedRows[0].Index;
int value = foo[idx];
foo.Remove(value);
foo.InsertAt(idx+1, value)

一些逻辑可能是错误的,这也可能不是最有效的方法。此外,它没有考虑到多行选择。
嗯,最后一件事,如果你使用标准列表或集合,这将不会很顺利。列表和集合不会发出DGV发现对数据绑定有用的事件。你可以在每次更改集合时“打嗝”数据绑定,但更好的解决方案是使用System.ComponentModel.BindingList。当您更改BindingList的排序时,DGV应该自动反映更改。

嗯...DGV的排序(如果允许)也可能对你的实现产生影响。 - Yoopergeek
我已经对BindingList进行了更改。我目前正在审查代码,以确保该更改不会影响其他功能。无论如何,我也倾向于重新排序集合而不是网格。哦,顺便说一下,它不允许多行选择(这是我的唯一好消息)。 - Silly Rabbit
是的,BindingList<T> 和 List<T> 稍有不同,它没有一些额外的辅助方法。我注意到最大的区别是它没有像 List<T> 那样接受 IEnumerable<T> 的构造函数。 - Yoopergeek
我继续使用BindingList并重新排序我的集合。目前测试结果非常好。我会继续关注它,看看它是否适用于我们的需求。感谢您的帮助。 - Silly Rabbit

3

在我看来,这里有一种更简单的方法。对于“向上”按钮点击执行操作基本上只是将行与上面的行交换。如果您自己控制值(如所述的问题),那么您只需要交换行的值即可。快速简单!

注意:仅当数据网格中禁用多选时才有效!正如您可以看到的那样,我只关注 SelectedRows 集合中索引为 0 的项目。

以下是我使用的内容:

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

要执行“向下”点击,你也可以做相反的操作,同样的逻辑。

1

首先填充您的DataGridView,例如您有一个包含3列的表格

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;

然后,在按钮向上点击时。
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;
    }
}

对于按钮向下,做同样的事情,只需在buttonDown_Click方法中将row indexrowIndex - 1更改为rowindex + 1


0

SchlaWiener的答案很好,我只想补充一些内容:

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();

}

在底部添加这3行代码,使选择移动(绑定源和数据网格视图),这样我们就可以不断点击底部来将一行向上移动。
要向下移动,只需调用bs.MoveNext()。
(我还没有足够的声望来发布评论)

1
欢迎来到SO:SE。嗯...正如您所知,作为答案发布的评论最终将被删除,即使它们像您的评论一样有用。获得50个声望并不是什么大问题,只需开始回答几个问题即可。 - mins

0
// Down
DataGridViewRow row = new DataGridViewRow();
int index = 0;

row = dgv.SelectedRows[0];
index = dgv.SelectedRows[0].Index;
dgv.Rows.Remove(dgv.SelectedRows[0]);
dgv.Rows.Insert(index + 1, row);
dgv.ClearSelection();
dgv.Rows[index + 1].Selected = true;

// Up
DataGridViewRow row = new DataGridViewRow();
int index = 0;

row = dgv.SelectedRows[0];
index = dgv.SelectedRows[0].Index;
dgv.Rows.Remove(dgv.SelectedRows[0]);
dgv.Rows.Insert(index-1, row);
dgv.ClearSelection();
dgv.Rows[index - 1].Selected = true;

其中dgv是你的DataGridView。


即使代码看起来很容易理解,也最好附上一个简短的解释 :) - juagicre

0

我在寻找这个上/下按钮的东西,很高兴我找到了它。 最好在返回之后放置bs.RaiseListChangedEvents = false语句,否则它不总是有效。

在C#3.0中,您可以像这样向BindingSource添加两个扩展方法:

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);
    }
}

终于有一个好的扩展方法使用了,而不是所有那些糟糕的字符串示例.. ;-)


0

这是我找到的问题最短的解决方案,我只是稍微重构了一下在此处找到的代码。

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>

并且有代码后端...

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();
    }

0
   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;
    }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接