1

I have a WinForm with DataGridView, my goal is to drag one column and drop it on other column index. I know column reordering is possible by using AllowUserToOrderColumns = true. But I have to perform other operations on DGV. That's why I need the target column index at a mouse-up event. To do that, I use HitTestInfo:

System.Windows.Forms.DataGrid.HitTestInfo myHitTest;
myHitTest = dataGrid1.HitTest(e.X, e.Y);
int p = myHitTest.ColumnIndex;

When I click on the first DGV column, this code runs and gives me the column's index (p). The problem is when I drop it on the other column of DGV, I'd like to know the target column's index, with the same code p = -1, I think because the HitTestInfo member returns a value on a MouseDown and not on a MouseUp. If anyone can tell me how to do it, it would be very great.

Dmitry
  • 13,797
  • 6
  • 32
  • 48
Ankit Singh
  • 25
  • 1
  • 6
  • You can add a MouseUp event by going to the designer view > select yout dgv > go to events> and double click MouseUp – Sybren Oct 19 '14 at 10:08

2 Answers2

2

You can create two HitTestInfo objects, one in the MouseDown and one in the MouseUp.

IMO, you also should use the DataGridView.HitTestInfo class, not DataGrid.HitTestInfo and try to not call or name DataGridViews DataGrids, which is a similar but different Control from WPF!

DataGridView.HitTestInfo myHitTestDown, myHitTestUp;
int visibleColumnDown, visibleColumnUp;

private void dataGrid1_MouseUp(object sender, MouseEventArgs e)
{
    myHitTestUp = dataGrid1.HitTest(e.X, e.Y);
    visibleColumnUp = getVisibleColumn(dataGrid1, e.X);
}

private void dataGrid1_MouseDown(object sender, MouseEventArgs e)
{
    myHitTestDown = dataGrid1.HitTest(e.X, e.Y);
    visibleColumnDown = getVisibleColumn(dataGrid1, e.X);
}

Update: To find the visible index of a column after the columns have been reordered simply use:

dataGrid1.Columns[myHitTestUp.ColumnIndex].DisplayIndex;

Before I found that, I wrote this little helper function, which does the same:

int getVisibleColumn(DataGridView dgv, int x)
{
    int cx = dgv.RowHeadersWidth;
    int c = 0;
    foreach (DataGridViewColumn col in dgv.Columns)
    {
        cx += col.Width; if ( cx >= x) return c; c++;
    }
    return -1;
}

To find out which Column was shuffled seems to be a bit harder. There is an event, which gets called for each column that was affected and it always gets called first for the one that was dragged along. Here is one way to do it:

Create to variables at class level:

List<DataGridViewColumn> shuffled = new List<DataGridViewColumn>();
DataGridViewColumn shuffledColumn = null;

Remember the first column:

private void dgvLoadTable_ColumnDisplayIndexChanged(
             object sender, DataGridViewColumnEventArgs e)
{
    if (shuffledColumn == null) shuffledColumn = e.Column;
}

Forget what happend before:

private void dgvLoadTable_MouseDown(object sender, MouseEventArgs e)
{  
    shuffledColumn = null;
}

Now you can use it. Selecting Columns is, however, not going well with shuffling them! If you do

 shuffledColumn.Selected = true;

it will only be selected if the SelectionMode is either FullColumnSelector ColumnHeaderSelect- In either mode the shuffling will not work, I'm afraid..

TaW
  • 53,122
  • 8
  • 69
  • 111
  • it works for drag n drop but when I do reshuffle same column continuously then ColumnIndex at mouseup gives wrong index where i am dropping. – Ankit Singh Oct 19 '14 at 11:37
  • Ah, that's right. Didn't notice the shuffling, sorry. I have added a helper function to my answer. – TaW Oct 19 '14 at 12:14
  • It's is very very useful for me. could you suggest a code that while reshuffle the column, shuffled column should be selected at mouse up event . – Ankit Singh Oct 19 '14 at 16:51
  • Actually it may be of interest but I found a much simpler way to find out the displayIndex of a column. See me edit. As for selecting a column, this is not as simple as it seems, because of the contradictions between `SelectionMode` and the reordering of the Columns. – TaW Oct 19 '14 at 18:07
  • I have made a sample here [Download sample project from here](https://drive.google.com/folderview?id=0B3CZaLjGYNiJamFPZXZQQ2g3RFU&usp=sharing). When I reshuffle same column then reshuffled columns are not getting selected correctly. Here columns are getting selected randomly. please correct this code. – Ankit Singh Oct 19 '14 at 18:16
  • int getVisibleColumn(DataGridView dgv, int x) { int cx = dgv.RowHeadersWidth; int c = 0; foreach (DataGridViewColumn col in dgv.Columns) { cx += col.Width; if ( cx >= x) return c; c++; } return -1; } it doesn't work when any column width is larger and any column which is smaller width drop at larger column then it gives wrong index plss correct it. – Ankit Singh Oct 22 '14 at 18:13
0

You could use drag'n'drop for that.
Assume you have a Form with a DataGridView named dataGridView1.

Initially don't forget to allow drag'n'drop for DataGridView:

dataGridView1.AllowDrop = true;

The event handler which replaces the desired functionality of MouseUp would be the dataGridView1_DragDrop, and the target column's index is colIndexOfItemUnderMouseToDrop:

private Rectangle dragBoxFromMouseDown;
private int colIndexFromMouseDown;

private void dataGridView1_MouseMove(object sender, MouseEventArgs e)
{
    if ((e.Button & MouseButtons.Left) == MouseButtons.Left)
    {
        // If the mouse moves outside the rectangle, start the drag.
        if (dragBoxFromMouseDown != Rectangle.Empty &&
            !dragBoxFromMouseDown.Contains(e.X, e.Y))
        {
            // Proceed with the drag and drop, passing in the list item.                    
            dataGridView1.DoDragDrop(colIndexFromMouseDown, DragDropEffects.Move);
        }
    }
}

private void dataGridView1_MouseDown(object sender, MouseEventArgs e)
{
    // Get the index of the item the mouse is below.
    colIndexFromMouseDown = dataGridView1.HitTest(e.X, e.Y).ColumnIndex;

    if (colIndexFromMouseDown != -1)
    {
        // Remember the point where the mouse down occurred. 
        // The DragSize indicates the size that the mouse can move 
        // before a drag event should be started.                
        Size dragSize = SystemInformation.DragSize;

        // Create a rectangle using the DragSize, with the mouse position being
        // at the center of the rectangle.
        dragBoxFromMouseDown = new Rectangle(
            new Point(e.X - (dragSize.Width / 2), e.Y - (dragSize.Height / 2)),
            dragSize);
    }
    else
        // Reset the rectangle if the mouse is not over an item in the ListBox.
        dragBoxFromMouseDown = Rectangle.Empty;
}

private void dataGridView1_DragOver(object sender, DragEventArgs e)
{
    e.Effect = DragDropEffects.Move;
}

private void dataGridView1_DragDrop(object sender, DragEventArgs e)
{
    // If the drag operation was a move then remove and insert the column.
    if (e.Effect == DragDropEffects.Move)
    {
        // The mouse locations are relative to the screen, so they must be 
        // converted to client coordinates.
        Point clientPoint = dataGridView1.PointToClient(new Point(e.X, e.Y));

        // Get the column index of the item the mouse is below. 
        int colIndexOfItemUnderMouseToDrop = dataGridView1.HitTest(clientPoint.X, clientPoint.Y).ColumnIndex;
        if (colIndexOfItemUnderMouseToDrop == -1)
            return;
        colIndexOfItemUnderMouseToDrop = dataGridView1.Columns[colIndexOfItemUnderMouseToDrop].DisplayIndex;
        // Now we have the column's display index.

        if (e.Data.GetDataPresent(typeof(int)))
        {
            int colToMove = (int)e.Data.GetData(typeof(int));
            dataGridView1.Columns[colToMove].DisplayIndex = colIndexOfItemUnderMouseToDrop;
            // Select the column:
            dataGridView1.Columns[colToMove].Selected = true;
        }
    }
}

EDIT
New approach:

private DataGridViewColumn columnToMove;

public Form1()
{
    InitializeComponent();

    dataGridView1.Columns.AddRange(new DataGridViewColumn[]
        {
            new DataGridViewTextBoxColumn { Name = "AAA", SortMode = DataGridViewColumnSortMode.NotSortable },
            new DataGridViewTextBoxColumn { Name = "BBB", SortMode = DataGridViewColumnSortMode.NotSortable },
            new DataGridViewTextBoxColumn { Name = "CCC", SortMode = DataGridViewColumnSortMode.NotSortable }
        });
    dataGridView1.Rows.Add(2);
    dataGridView1.AllowUserToOrderColumns = true;
    dataGridView1.MouseDown += dataGridView1_MouseDown;
    dataGridView1.ColumnDisplayIndexChanged += dataGridView1_ColumnDisplayIndexChanged;
}

private void dataGridView1_ColumnDisplayIndexChanged(object sender, DataGridViewColumnEventArgs e)
{
    if (e.Column == columnToMove)
    {
        dataGridView1.SelectionMode = DataGridViewSelectionMode.ColumnHeaderSelect;
        e.Column.Selected = true;
    }
}

private void dataGridView1_MouseDown(object sender, MouseEventArgs e)
{
    var hti = dataGridView1.HitTest(e.X, e.Y);
    if (hti.Type == DataGridViewHitTestType.ColumnHeader)
    {
        columnToMove = dataGridView1.Columns[hti.ColumnIndex];
        dataGridView1.SelectionMode = DataGridViewSelectionMode.RowHeaderSelect;
    }
}
Dmitry
  • 13,797
  • 6
  • 32
  • 48
  • Thanks...it works for drag n drop but when I do reshuffle continuously then int colIndexOfItemUnderMouseToDrop = dataGridView1.HitTest(clientPoint.X, clientPoint.Y).ColumnIndex gives wrong index where i am dropping. – Ankit Singh Oct 19 '14 at 11:35
  • Look at the next 3 lines. The `colIndexOfItemUnderMouseToDrop` is being corrected there: `colIndexOfItemUnderMouseToDrop = dataGridView1.Columns[colIndexOfItemUnderMouseToDrop].DisplayIndex;` – Dmitry Oct 19 '14 at 11:57
  • @AnkitSingh was it clear enough? If so, was the answer helpful? – Dmitry Oct 19 '14 at 14:19
  • It's is very useful for me. could you suggest a code that while reshuffle the column, shuffled column should be selected at mouse up event . – Ankit Singh Oct 19 '14 at 16:17
  • @AnkitSingh You could set the `Selected = true` for the desired column. I've updated my answer. Please revise. If I answered your initial question, you should mark it as accepted: [What should I do when someone answers my question?](http://stackoverflow.com/help/someone-answers). If you have another question, please ask it separately. – Dmitry Oct 19 '14 at 16:38
  • Please check it doesn't work when we do same column reshuffle . it do selected any column randomly. – Ankit Singh Oct 19 '14 at 16:50
  • @AnkitSingh I cannot understand what exactly you are talking about. Please download the [sample project](https://drive.google.com/file/d/0B2SR1OxxycOISjB1RnFic2EzVlk/view?usp=sharing) to make sure everything works as expected – Dmitry Oct 19 '14 at 17:26
  • Could you plss help me out with this sample [Sample project](https://drive.google.com/folderview?id=0B3CZaLjGYNiJamFPZXZQQ2g3RFU&usp=sharing). please correct this . It doesn't work while reshuffling same column, reshuffled column not get selected properly .it get selected randomly . plss plss – Ankit Singh Oct 19 '14 at 17:55
  • @AnkitSingh I've added a new approach. Please revise. – Dmitry Oct 19 '14 at 18:17
  • Dmitry I need one help plss? – Ankit Singh Oct 23 '14 at 17:27