У меня есть DataGridView, источником данных которого является DataTable. Этот DataTable имеет логический столбец, который интерпретируется как флажок в DataGridView.
employeeSelectionTable.Columns.Add("IsSelected", typeof(bool));
...
employeeSelectionTable.RowChanged += selectionTableRowChanged;
dataGridViewSelectedEmployees.DataSource = employeeSelectionTable;
...
private void selectionTableRowChanged(object sender, DataRowChangeEventArgs e)
{
if ((bool)e.Row["IsSelected"])
{
Console.Writeline("Is Selected");
}
else
{
Console.Writeline("Is Not Selected");
}
break;
}
Когда пользователь щелкает по флажку один раз, он проверяется, и selectionTableRowChanged выводит «Выбрано».
Точно так же, когда пользователь проверяет его снова, поле очищается, и selectionTableRowChanged выводит «Is Not Selected».
Вот где у меня проблема:
Когда пользователь дважды щелкает по флажку, флажок устанавливается, вызывается событие RowChanged («Выбрано»), затем флажок сбрасывается, и соответствующее событие RowChanged не вызывается. Теперь подписчик на событие RowChanged не синхронизирован.
Мое решение прямо сейчас - создать подкласс DataGridView и переопределить WndProc, чтобы съесть WM_LBUTTONDBLCLICK, поэтому любой двойной щелчок по элементу управления игнорируется. Есть ли лучшее решение?





Есть ли причина, по которой это нужно делать на таком низком уровне? Может ли метод DoubleClick быть просто пустым методом, который его съедает?
Я уже пробовал переопределить метод OnDoubleClick в подклассе DataGridView, чтобы ничего не делать, но он все же позволил изменить флажок во второй раз.
В идеале я хотел, чтобы событие DataTable RowChanged вызывалось дважды. Есть ли способ повлиять на базовый DataTable с помощью переопределенного метода OnDoubleClick?
Почему бы просто не оставить столбец IsSelected неограниченным? Используйте событие CellContentClick, чтобы передать данные в базовый объект данных и оставить событие CellDoubleClick или RowChange в покое.
private void dgv_CellContentClick(object sender, DataGridViewCellEventArgs e)
{
if (e.ColumnIndex == <columnIndex of IsSelected>)
{
string value = dgv[e.ColumnIndex, e.RowIndex].EditedFormattedValue;
if ( value == null || Convert.ToBoolean(value) == false)
{
//push false to employeeSelectionTable
}
else
{
//push true to employeeSelectionTable
}
}
}
Причина, по которой создание пустого метода события DoubleClick не поможет, заключается в том, что он выполняется в дополнение к другим операциям, которые происходят при двойном щелчке.
Если вы посмотрите на сгенерированный Windows код или примеры программного добавления обработчиков событий, вы используете + = для назначения обработчика событий. Это означает, что вы добавляете этот обработчик событий в дополнение к другим, которые уже существуют, у вас может быть несколько обработчиков событий, запускаемых при событии сохранения.
Моим инстинктом было бы переопределить класс DataGridView, а затем переопределить метод OnDoubleClick, а не вызывать базовый метод OnDoubleClick.
Тем не менее, я проверил это очень быстро и вижу некоторые интересные результаты.
Я собрал следующий тестовый класс:
using System;
using System.Windows.Forms;
namespace TestApp
{
class DGV : DataGridView
{
private string test = "";
protected override void OnDoubleClick(EventArgs e)
{
MessageBox.Show(test + "OnDoubleClick");
}
protected override void OnCellMouseDoubleClick(System.Windows.Forms.DataGridViewCellMouseEventArgs e)
{
MessageBox.Show(test + "OnCellMouseDoubleClick");
}
protected override void OnCellMouseClick(System.Windows.Forms.DataGridViewCellMouseEventArgs e)
{
if (e.Clicks == 1)
{
// Had to do this with a variable as using a MessageBox
// here would block us from pulling off a double click
test = "1 click ";
base.OnCellMouseClick(e);
}
else
{
MessageBox.Show("OnCellMouseClick");
}
}
}
}
Затем вставил это в форму Windows, добавил столбец флажка и запустил программу.
При новом запуске двойной щелчок по флажку приводит к отображению сообщения «1 щелкните OnDoubleClick».
Это означает, что OnCellMouseClick выполняется при первой части двойного щелчка, а затем OnDoubleClick выполняется при втором щелчке.
Кроме того, к сожалению, удаление вызова базовых методов, похоже, не мешает флажку получить переданный ему щелчок.
Я подозреваю, что для того, чтобы этот подход сработал, возможно, придется пойти дальше и переопределить DataGridViewCheckBoxColumn и DataGridViewCheckBoxCell, которые игнорируют двойной щелчок. Предполагая, что это работает, вы сможете остановить двойной щелчок по флажку, но разрешить его использование в других элементах управления столбцом.
Я опубликовал ответ на другой вопрос, в котором говорится о создании настраиваемых столбцов и ячеек DataGridView в здесь.
Не знаю, почему мне пришлось, но я смог добавить событие таймера, прикрепленное к обновлению datagridview, которое только что обновило dgv после второго щелчка.
В событии щелчка ячейки
** _RefreshTimer = new Timer();
_RefreshTimer.Tick += new EventHandler(RefreshTimer_Tick);
_RefreshTimer.Interval = 100;
_RefreshTimer.Start();
}
}
}
void RefreshTimer_Tick(object sender, EventArgs e)
{
dgv.Refresh();
_RefreshTimer.Stop();
_RefreshTimer = null;
}**
Если вам нужен столбец флажка внутри DataGridView, создайте что-то вроде этого:
DataGridViewCheckBoxCell checkBoxCell = new MyDataGridViewCheckBoxCell();
...
DataGridViewColumn col = new DataGridViewColumn(checkBoxCell);
...
col.Name = "colCheckBox";
...
this.dgItems.Columns.Add(col);
где dgItems - это экземпляр DataGridView.
Как видите, у меня есть класс MyDataGridViewCheckBoxCell, который является подклассом класса DataGridViewCheckBoxCell. В этом подклассе я определяю:
protected override void OnContentDoubleClick(DataGridViewCellEventArgs e)
{
//This the trick to keep the checkbox in sync with other actions.
//base.OnContentDoubleClick(e);
}
Теперь, когда пользователь дважды щелкает флажок в столбце флажка, флажок будет вести себя так, как если бы он был нажат однократно. Это должно решить вашу проблему с синхронизацией.
Это не совсем элегантное решение, но почему бы просто не сделать следующее?
private void dgv_CellDoubleClick(object sender, DataGridViewCellEventArgs e)
{
dgv_CellClick(sender, e);
}
Это явно вызовет второе событие CellClick и предотвратит рассинхронизацию.