I am trying to display data in a table format and allow users to pick cells. However, they should only be able to pick 1 cell per column, which is why MultiSelect is required.
Example of 1 cell selection per column:
Currently, I have an implementation for allowing only 1 selected cell per column, which works until I try to drag select.
Example of drag selection:
From what I’ve seen, there is no built-in way to disable drag selection if MultiSelect needs to be enabled.
Here is the current implementation:
private void TableViewer_CellMouseDown(object sender, DataGridViewCellMouseEventArgs e)
{
// Skip headers
if (e.RowIndex < 0 || e.ColumnIndex < 0)
{
return;
}
// Disable multiple selections within the same column
var selectedCells = gridViewTableViewer.SelectedCells.Cast<DataGridViewCell>();
foreach (DataGridViewCell cell in selectedCells)
{
if (cell.ColumnIndex == e.ColumnIndex)
{
// Another cell in the same column has already been selected.
cell.Selected = false;
}
}
}
I’ve tried keeping track of the left mouse button (LMB) being held down/pressed through the CellMouseDown
and MouseUp
events + a TableViewer.ClearSelection()
inside the CellMouseEnter
event (if LMB is being held down):
(TableViewer is a DataGridView)
private void TableViewer_CellMouseEnter(object sender, DataGridViewCellEventArgs e)
{
if (isMouseDown)
{
TableViewer.ClearSelection();
}
}
private void TableViewer_CellMouseDown(object sender, DataGridViewCellMouseEventArgs e)
{
isMouseDown = true;
...
}
private void TableViewer_MouseUp(object sender, MouseEventArgs e)
{
isMouseDown = false;
}
Any help would be appreciated – thank you so much!
Matt is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.
1
If you’re willing to extend DataGridView
you could try detecting mouse drag and disabling cell selection for a brief interval after detection. It worked with the test code shown below.
class DataGridViewEx : DataGridView
{
protected override void SetSelectedCellCore(int columnIndex, int rowIndex, bool selected)
{
bool disabledByOneCellPerColumnRule =
ModifierKeys == Keys.Control &&
SelectedCells.OfType<DataGridViewCell>().Any(_ => _.ColumnIndex == columnIndex);
base.SetSelectedCellCore(
columnIndex,
rowIndex,
selected && !(disabledByOneCellPerColumnRule || _wdtMove.Running));
}
protected override void OnMouseMove(MouseEventArgs e)
{
if (MouseButtons == MouseButtons.Left) _wdtMove.StartOrRestart();
base.OnMouseMove(e);
}
// <PackageReference Include="IVSoftware.Portable.WatchdogTimer" Version="1.2.1" />
WatchdogTimer _wdtMove = new WatchdogTimer{ Interval = TimeSpan.FromMilliseconds(250)};
}
You would then just manually edit your MainForm.Designer.cs file, substituting DataGridViewEx
for DataGridView
in two places.
Test Code
public partial class MainForm : Form
{
public MainForm() => InitializeComponent();
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
dataGridView.DataSource = Records;
dataGridView.AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.Fill;
for (int i = 0; i< 10; i++)
{
Records.AddNew();
}
}
BindingList<Record> Records { get; } = new BindingList<Record>();
}
public class Record
{
static int _debugCount = 1;
public Record()
{
col1 = col2 = col3 = _debugCount ++;
}
public int col1 { get; set; }
public int col2 { get; set; }
public int col3 { get; set; }
}
Variant – Drag-Select follows “One Cell per Column” rule
Here’s a slight variation. See if it’s more intuitive this way or the other.
class DataGridViewEx : DataGridView
{
protected override void SetSelectedCellCore(int columnIndex, int rowIndex, bool selected)
{
bool disabledByOneCellPerColumnRule =
((ModifierKeys == Keys.Control) || _wdtMove.Running) &&
SelectedCells.OfType<DataGridViewCell>().Any(_ => _.ColumnIndex == columnIndex);
base.SetSelectedCellCore(
columnIndex,
rowIndex,
selected && !disabledByOneCellPerColumnRule);
}
protected override void OnMouseMove(MouseEventArgs e)
{
if (MouseButtons == MouseButtons.Left) _wdtMove.StartOrRestart();
base.OnMouseMove(e);
}
// <PackageReference Include="IVSoftware.Portable.WatchdogTimer" Version="1.2.1" />
WatchdogTimer _wdtMove = new WatchdogTimer { Interval = TimeSpan.FromMilliseconds(250) };
}