-4

I am writing a reservation application which utilizes a DataGridView to list the available rooms down the Y axis and the available times on the X axis as the columns.

I would like the user to be able to drag select a time frame, but it has to be limited to one row at a time.

Either controlling the highlight aspect of the grid so only the desired row is highlighted when the mouse moves or capturing the mouse within the rows bounds are the options I have thought of. Any help in implementation either of these or even a new way to handle the task are all welcome!

I would prefer to just capture the mouse with the DataRow where the mouse down event occurs, not sure if I have to use a clipping rectangle to achieve this or not.

Thanks in advance for any help.

Cody
  • 63
  • 10
  • 4
    Don't restrict the users actual mouse movement, that will confuse your users a lot. Handle an event that occurs while you are dragging, check the row number and only allow selection for the current row. – Bernd Linde Apr 14 '15 at 15:25
  • I agree that restricting user movement is a bad play. Which event? SelectionChanged? CellStateChanged? And how do you just not allow any rows to be selected except one? I mean its easy to deselect unwanted cells , but to actually prevent a row from being selected please elaborate, because that's exactly what I need. – Cody Apr 14 '15 at 17:45
  • Selecting an entire row should be prevented by either the grid setup or your event handlers. Which events to handle for the cell selection I would recommend initially handling all the events and see which one is fired when (or read their corresponding documentation), that should then direct you towards which you can use for cross-row selection prevention. – Bernd Linde Apr 14 '15 at 20:03
  • Thank you for responding Bernd. It was the CellStateChanged event and is working perfectly. Thanks for the right push in the right direction my brain was just done and needed a little boost. – Cody Apr 14 '15 at 20:31

2 Answers2

1

Probably a better way to write this but it works.

private void dataGridView1_CellStateChanged(object sender, DataGridViewCellStateChangedEventArgs e)
    {
        if (dataGridView1.SelectedCells.Count > 1)
        {
            //Retrieves the first cell selected
            var startRow = dataGridView1.SelectedCells[dataGridView1.SelectedCells.Count - 1].RowIndex;

            foreach (DataGridViewCell cell in dataGridView1.SelectedCells)
            {
                if (cell.RowIndex != startRow)
                {
                    cell.Selected = false;
                }
            }
        }
    }
Cody
  • 63
  • 10
  • As a note to your comment in the code: The order of the `SelectedCells` list is not guaranteed to be in the order that the user selected, so taking the last one as the first one selected could give you random bugs. [MSDN reference](https://msdn.microsoft.com/en-us/library/system.windows.forms.datagridviewselectedcellcollection%28v=vs.110%29.aspx) (Under Remarks) – Bernd Linde Apr 15 '15 at 01:05
1

As an improvement on your CellStateChanged event code, the below could be used.

private void dataGridView1_CellStateChanged(object sender, DataGridViewCellStateChangedEventArgs e)
{
  if ((e.StateChanged == DataGridViewElementStates.Selected) && // Only handle it when the State that changed is Selected
      (dataGridView1.SelectedCells.Count > 1))
  {
    // A LINQ query on the SelectedCells that does the same as the for-loop (might be easier to read, but harder to debug)
    // Use either this or the for-loop, not both
    if (dataGridView1.SelectedCells.Cast<DataGridViewCell>().Where(cell => cell.RowIndex != e.Cell.RowIndex).Count() > 0)
    {
      e.Cell.Selected = false;
    }

    /*
    foreach (DataGridViewCell cell in dataGridView1.SelectedCells)
    {
      if (cell.RowIndex != e.Cell.RowIndex)
      {
        e.Cell.Selected = false;
        break;  // stop the loop as soon as we found one
      }
    }
    */
  }
}

The differences in this for-loop is the usage of e.Cell as a reference point for RowIndex since e.Cell is the cell that was selected by the user, setting of e.Cell.Selected to false instead of cell.Selected and finally a break; in the for-loop since after the first RowIndex mismatch, we can stop the checking.

Bernd Linde
  • 2,098
  • 2
  • 16
  • 22