Maintain multi select on mouse click and perform further functionalities

Viewed 1313

Hi I am making an application in C#.

What I am trying to achieve

  • I select Multiple rows with CTRL among results in DataGridView
  • then I right click on it and it shows options
  • I Click onto choose selected option
  • The chosen rows stays on the DataGridView and other rows are removed

Problem where I got stuck

  • I select Multiple rows with CTRL
  • But when I Right Click on the rows the multiple selection vanishes

Note: I am using MouseClick Event for right click on rows and generate a menu. The code of this event is given below:

private void SearchPanelDataGridView_MouseClick(object sender, MouseEventArgs e)
{
    if (e.Button == MouseButtons.Right)
    {
        ContextMenuStrip smenu = new System.Windows.Forms.ContextMenuStrip();
        var htest = SearchPanelDataGridView.HitTest(e.X, e.Y);
        SearchPanelDataGridView.ClearSelection();
        SearchPanelDataGridView.Rows[htest.RowIndex].Selected = true;

        smenu.Items.Add("Clear Record").Name = "Clear Record";
        smenu.Items.Add("Pay Amount").Name = "Pay Amount";
        smenu.Items.Add("Break Apart").Name = "Break Apart";
        smenu.Items.Add("View Details").Name = "View Details";
        smenu.Items.Add("Choose Selected").Name = "Choose Selected"; // Choose Rows Option
        smenu.Items.Add("Reset").Name = "Reset";

        int CurrentMouseOverRow = SearchPanelDataGridView.HitTest(e.X, e.Y).RowIndex;
        smenu.Show(SearchPanelDataGridView, new Point(e.X, e.Y));
        smenu.ItemClicked += new ToolStripItemClickedEventHandler(smenu_ItemClicked);
    }
}
3 Answers

You don't need to handle MouseClick event. Just create the ContextmenuStrip using designer or in load event of the form. Then assign the context menu strip to ContextMenuStrip property of the DataGridView, then handle Click event of the ToolStripMenuItem which should remove non-selected rows and use such code:

dataGridView1.Rows.Cast<DataGridViewRow>()
    .Where(x => !dataGridView1.SelectedRows.Contains(x) && !x.IsNewRow)
    .ToList().ForEach(x => dataGridView1.Rows.Remove(x));

This way, all non-selected rows will be removed and the selection will remain untouched.

It seems hard to avoid this behavior entirely but you can use the below workaround and restore the selected rows after DataGridView deselects them:

private bool SUSPEND_EVENT_HANDLING;
private List<DataGridViewRow> dgvMyGridSelectedRows = new List<DataGridViewRow>();

private void dgvMyGrid_CellMouseDown(object sender, DataGridViewCellMouseEventArgs e)
{
    dgvMyGridSelectedRows.Clear();

    // if the mouse is down on a selected row then remember all selected rows
    if (dgvMyGrid.Rows(e.RowIndex).Selected)
        dgvMyGridSelectedRows.AddRange(dgvMyGrid.SelectedRows.Cast<DataGridViewRow>);
}

private void dgvMyGrid_SelectionChanged(object sender, EventArgs e)
{
    if (!IsHandleCreated || SUSPEND_EVENT_HANDLING)
        return;

    // restore the remembered rows selection
    if (dgvMyGridSelectedRows.Any())
    {
        SUSPEND_EVENT_HANDLING = true;
        dgvMyGrid.ClearSelection();
        dgvMyGridSelectedRows.ForEach(x => x.Selected = true);
        SUSPEND_EVENT_HANDLING = false;
    }
}

The SUSPEND_EVENT_HANDLING is to avoid stack overflow because you select rows inside of the SelectionChanged event handler.

This might not be the best solution, but for now you could try this:

    List<int> selectedRowsIndexes = new List<int>();
    private void dataGridView1_Click(object sender, EventArgs e)
    {
        selectedRowsIndexes.Clear();
        foreach (DataGridViewRow row in dataGridView1.SelectedRows)
           selectedRowsIndexes.Add(row.Index);
    }

    private void dataGridView1_MouseClick(object sender, MouseEventArgs e)
    {
        if (e.Button == MouseButtons.Right)
        {
            ContextMenuStrip smenu = new System.Windows.Forms.ContextMenuStrip();
            var htest = dataGridView1.HitTest(e.X, e.Y);
            dataGridView1.ClearSelection();
            dataGridView1.Rows[htest.RowIndex].Selected = true;

            smenu.Items.Add("Clear Record").Name = "Clear Record";
            smenu.Items.Add("Pay Amount").Name = "Pay Amount";
            smenu.Items.Add("Break Apart").Name = "Break Apart";
            smenu.Items.Add("View Details").Name = "View Details";
            smenu.Items.Add("Choose Selected").Name = "Choose Selected"; // Choose Rows Option
            smenu.Items.Add("Reset").Name = "Reset";

            int CurrentMouseOverRow = dataGridView1.HitTest(e.X, e.Y).RowIndex;
            smenu.Show(dataGridView1, new Point(e.X, e.Y));

            foreach (int rowIndex in selectedRowsIndexes)
            {
                for (int i = 0; i < dataGridView1.Rows.Count; i++)
                {
                   dataGridView1.Rows[i].Selected = (i == rowIndex);
                }
            }
        }
    }

For this to work, the DataGridView's SelectionMode property should be set to FullRowSelect.

Related