實現 DataGridView 的拖曳調整順序

  • 6682
  • 0
  • C#
  • 2021-06-10

實現 DataGridView 的拖曳調整順序

使用 DataGridView 來顯示資料是一個非常方便的模式,雖然 DataGridView 本身已經有提供排序功能,但總是會有一種情況就是無法依照自己的想法來排列。

基於這個想法,就來實做在 DataGridView 中透過拖曳方式來調整行的順序。

 

必要條件:

  • DataGridView 控制項
  • 必須強制設定為單選
  • 必須啟用 DataGridView 的 AllowDrop 功能

 

首先來分析一下動作,第一先使用滑鼠左鍵來點選某個欄位或是某一行,然後滑鼠鍵按住後開始移動游標,此時游標要呈現拖曳中的圖樣,移動到目的行後放開滑鼠鍵,將先前點選的行資料剪下來,然後插入到目前行之後。

滑鼠點選與移動時,可以使用 DataGridView.HitTest 代入滑鼠目前X,Y座標,來取回目前游標於 DataGridView 所在位置的資訊,就可以判斷是處於行首或是列首了。

其中判斷是否為新行,則是可以透過 DataGridView.Rows.IsNewRow 屬性來得知。至於產生拖曳效果則是在 MouseMove 事件中處理,使用 DoDragDrop 方法,並且將已選取的資料行代入,如此就可以產生拖曳行為。

接下來緊接著觸發了 DragEnter 事件,在這個事件中只需要定義拖曳時的效果模式。

最後當放開滑鼠左鍵之後,就引發了 DragDrop 事件,這也是最後的關鍵。先判傳進來的資料是否為 DataGridViewRow 型態,不是的話就直接結束不做處理。再由 PointToClient 取回目前所點選到的位置,判斷目前位置如果是行首、列首是新增行,又或是原本的資料行時就立刻結束不做處理。

 

  • DataGridView 沒有設定 DataSource 直接由使用者輸入資料

直接對 DataGridViewRow 來進行處理。

將已選取的資料行取出,然後在 DataGridView 中刪除該行,最後將取出的資料行插入到目前點選位置之後。

  • DataGridView 透過 BindingSource 來繫結資料或是直接連結 DataTable

先將 DataSource 物件轉為 DataTable,再設定 DataTable 的 PrimaryKey,透過目前選取的資料在 DataTable 中找出符合的資料行。複製一份該資料行,然後刪除 DataTable 中所符合的資料行,完成後再將剛剛複製的資料行插入到目前所點選位置之後。

 

例:

20141002_005

3.周五 拖曳到 7.王八 的位置上

20141002_006

3.周五 就會擠在 7.王八8.陳七 中間。

 

MousDown 事件


{
    if (dataGridView1.HitTest(e.X, e.Y).Type != DataGridViewHitTestType.Cell) return;

    if (e.Button == MouseButtons.Left)
    {
        if (dataGridView1.Rows[dataGridView1.HitTest(e.X, e.Y).RowIndex].IsNewRow) return;
    }
}

 

MouseMove 事件


{
    if (dataGridView1.HitTest(e.X, e.Y).Type != DataGridViewHitTestType.Cell) return;

    if (e.Button == MouseButtons.Left)
    {
        dataGridView1.Rows[dataGridView1.SelectedCells[0].RowIndex].Selected = true;
        dataGridView1.DoDragDrop(dataGridView1.SelectedRows[0], DragDropEffects.Move);
    }
}

 

DragEnter 事件


{
    e.Effect = DragDropEffects.Move;
}

 

DragDrop 事件


{
    if (((DataObject)e.Data).GetData(typeof(DataGridViewRow)) == null) return;

    Point TargetPoint = dataGridView1.PointToClient(new Point(e.X, e.Y));
    if ((dataGridView1.HitTest(TargetPoint.X, TargetPoint.Y).Type != DataGridViewHitTestType.Cell)) return;

    int TargetRowIndex = dataGridView1.HitTest(TargetPoint.X, TargetPoint.Y).RowIndex;
    if ((dataGridView1.Rows[TargetRowIndex].IsNewRow) || (dataGridView1.SelectedRows.Contains(dataGridView1.Rows[TargetRowIndex]))) return;

    if (dataGridView1.DataSource == null)
    {
        DataGridViewRow SourceRow = dataGridView1.SelectedRows[0];
        dataGridView1.Rows.Remove(SourceRow);
        dataGridView1.Rows.Insert(TargetRowIndex, SourceRow);
    }
    else
    {
        DataTable SourceData = dataGridView1.DataSource.GetType() == typeof(BindingSource) ? ((DataSet)((BindingSource)dataGridView1.DataSource).DataSource).Tables[0] : (DataTable)dataGridView1.DataSource;
        SourceData.PrimaryKey = new DataColumn[] { SourceData.Columns[0] };

        DataRow OriginRow = SourceData.Rows.Find(dataGridView1.SelectedRows[0].Cells[0].Value);
        if (OriginRow == null) return;

        DataRow SourceRow = SourceData.NewRow();
        int InsertIndex = SourceData.Rows.IndexOf(SourceData.Rows.Find(dataGridView1.Rows[TargetRowIndex].Cells[0].Value));
        SourceRow.ItemArray = OriginRow.ItemArray;
        SourceData.Rows.Remove(OriginRow);
        SourceData.Rows.InsertAt(SourceRow, InsertIndex);
        
        SourceData.AcceptChanges();
    }
    dataGridView1.CurrentCell = dataGridView1.Rows[TargetRowIndex].Cells[dataGridView1.CurrentCell.ColumnIndex];
}

程式是運氣與直覺堆砌而成的奇蹟。
若不具備這兩者,不可能以這樣的工時實現這樣的規格。
修改規格是對奇蹟吐槽的褻瀆行為。
而追加修改則是相信奇蹟還會重現的魯莽行動。