通过DataGridView重新排序数据表中的列

本文关键字:数据表 排序 DataGridView 新排序 通过 | 更新日期: 2023-09-27 17:50:40

我有一个DataGridView,并启用了AllowUserToOrderColumns属性,以便用户可以使用拖放重新排序列。我已经为ColumnDisplayIndexChanged事件创建了一个处理程序。DataGridView被绑定到一个数据表。当用户在DataGridView中重新排序列时,我想重新排序数据表中的列。

这是我目前得到的:

private void dataGridView_Main_ColumnDisplayIndexChanged(object sender, DataGridViewColumnEventArgs e)
{
    if(!_isDatabinding)
    {
        if(_data.Columns[columnName].Ordinal != e.Column.DisplayIndex)
        {
            _data.Columns[e.Column.DataPropertyName].SetOrdinal(e.Column.DisplayIndex);
        }
    }
}

问题是这会导致以下异常:

"Collection was modified;不能执行枚举操作。"

我意识到这正在发生,因为调用SetOrdinal方法导致事件处理程序被再次调用,因为数据表被绑定到DataGridView。所以我把它改成如下:

private void dataGridView_Main_ColumnDisplayIndexChanged(object sender, DataGridViewColumnEventArgs e)
{
    if(!_isDatabinding && !_isChangingColumnOrder)
    {
        if(_data.Columns[columnName].Ordinal != e.Column.DisplayIndex)
        {
            _isChangingColumnOrder = true;
            _data.Columns[e.Column.DataPropertyName].SetOrdinal(e.Column.DisplayIndex);
            _isChangingColumnOrder = false;
        }
    }
}

现在在DataGridViewColumn::DisplayIndex属性setter中抛出一个NullReferenceException。这是callstack:

System.Windows.Forms.dll!System.Windows.Forms.DataGridViewColumn.DisplayIndex.set(int value) + 0xc5 bytes   
System.Windows.Forms.dll!System.Windows.Forms.DataGridView.EndColumnRelocation(System.Windows.Forms.MouseEventArgs e, System.Windows.Forms.DataGridView.HitTestInfo hti) + 0x27f bytes  
System.Windows.Forms.dll!System.Windows.Forms.DataGridView.OnMouseUp(System.Windows.Forms.MouseEventArgs e) + 0x3a6 bytes   
System.Windows.Forms.dll!System.Windows.Forms.Control.WmMouseUp(ref System.Windows.Forms.Message m, System.Windows.Forms.MouseButtons button, int clicks) + 0x48b bytes 
System.Windows.Forms.dll!System.Windows.Forms.Control.WndProc(ref System.Windows.Forms.Message m) + 0xe14 bytes 
System.Windows.Forms.dll!System.Windows.Forms.DataGridView.WndProc(ref System.Windows.Forms.Message m) + 0x134 bytes    
System.Windows.Forms.dll!System.Windows.Forms.NativeWindow.DebuggableCallback(System.IntPtr hWnd, int msg = 514, System.IntPtr wparam, System.IntPtr lparam) + 0x14c bytes  

我不确定是什么原因导致了这个异常。

我还尝试在调用SetOrdinal之前在事件处理程序中设置DataGridView的DataSource属性为null,然后在方法结束时重新绑定它。试图避免问题与数据表被绑定到DataGridView时重新排序。但这仍然会抛出相同的NullReferenceException。无论如何,这可能不是理想的,因为每个受影响的列调用处理程序一次。总是不止一次),而且似乎没有任何我可以处理的事件来告诉我列重新排序何时开始和结束。

我能让它工作的唯一方法是做以下(注意复制):

private void dataGridView_Main_ColumnDisplayIndexChanged(object sender, DataGridViewColumnEventArgs e)
{
    if(!_isDatabinding)
    {
        if(_data.Columns[columnName].Ordinal != e.Column.DisplayIndex)
        {
            var newData = _data.Copy();
            newData.Columns[e.Column.DataPropertyName].SetOrdinal(e.Column.DisplayIndex);
            _data = newData;
        }
    }
}

我真的不想做这个复制,因为数据可能相当大。

我有没有办法不用拷贝就能做到?

通过DataGridView重新排序数据表中的列

将datagridview重新绑定到datatable

 DataTable dt = new DataTable();
 foreach(DataGridViewColumn col in dgv.Columns)
    {
   dt.Columns.Add(col.HeaderText);    
    }
   foreach(DataGridViewRow row in dgv.Rows)
   {
     DataRow dRow = dt.NewRow();
    foreach(DataGridViewCell cell in row.Cells)
     {
      dRow[cell.ColumnIndex] = cell.Value;
     }
    dt.Rows.Add(dRow);
   }