DataGridView бросает "InvalidOperationException: операция недействительна..." при добавлении строки
Я хочу, чтобы OpenFileDialog появился, когда пользователь нажимает на ячейку, а затем отображает результат в ячейке.
Все работает, за исключением того, что DataGridView отображает дополнительную строку для добавления значений в список, к которому он привязан. Строка отображается, если dataGridView.AllowUserToAddNewRows == true
, что я и хочу. Я не хочу, чтобы приложение зависало, когда эта строка редактируется программно; вместо этого он должен делать то, что он сделал бы, если бы пользователь отредактировал эту строку вручную (добавьте новую строку в базовый список, нажмите еще одну пустую строку в сетку для добавления значений).
Я прочитал о SendKeys.Send(), который должен заставить DataGridView вести себя точно так, как если бы пользователь набрал значение; однако он тоже не работает. Вот что я пытаюсь:
if (openFileDialog1.ShowDialog() == DialogResult.OK)
{
dataGridView1.CurrentCell = cell;
//simply doing a cell.Value = etc. will cause the program to crash
cell.ReadOnly = false;
dataGridView1.Columns[cell.ColumnIndex].ReadOnly = false;
dataGridView1.EditMode = DataGridViewEditMode.EditOnEnter;
dataGridView1.BeginEdit(true);
SendKeys.Send(openFileDialog1.FileName + "{Enter}");
dataGridView1.EndEdit();
cell.ReadOnly = true;
dataGridView1.Columns[cell.ColumnIndex].ReadOnly = true;
}
//I would expect the FileName would be in the cell now, and a new empty
//row tacked onto the end of the DataGridView, but it not; the DataGridView
//is not changed at all.
Ответы
Ответ 1
У меня была такая же проблема при попытке программно редактировать ячейки с источником привязки.
"Операция недействительна из-за текущего состояния объекта"
Какая операция? Какое состояние? Настолько полезно.
Мой код работает нормально, кроме как при редактировании последней строки в сетке.
Выключает ключ DataGridView.NotifiyCurrentCelldirty(true)
Правильная последовательность для программного редактирования ячейки, поэтому она работает так же, как если бы пользователь это сделал.
(При изменении ячейки в последней строке появляется новая пустая строка) выглядит примерно так:
1) Сделайте ячейку для редактирования текущей ячейки (сделайте то, что вам нужно для текущей текущей ячейки, сначала как вызов endEdit, если он находится в режиме редактирования.)
2) Вызов DataGridview.BeginEdit(false)
3) Вызов DataGridView.NotifyCurrentCellDirty(true)
4) Измените значение.
5) Вызовите DataGridView.EndEdit()
И вы хотите что-то сделать для событий RowValidating и RowValidated.
Одна из моих подпрограмм для обновления значения ячейки выглядит следующим образом:
Это из метода в моем классе, полученного из DataGridView.
Вы можете сделать то же самое из содержащейся формы, вызвав
через экземпляр DataGridView, потому что методы являются общедоступными.
Здесь вызовы используют impliciit 'this.'
private void EnterTime()
{
if (CurrentRow == null) return;
SaveCurrentCell(); // Calls EndEdit() if CurrentCell.IsInEditMode
DataGridViewCell previous = CurrentCell;
CurrentCell = CurrentRow.Cells[CatchForm.TimeColumn];
BeginEdit(false);
NotifyCurrentCellDirty(true);
CurrentCell.Value = DateTime.Now;
EndEdit();
CurrentCell = previous;
}
Я не уверен, зачем нужен отдельный вызов.
Почему doesn BeginEdit, или фактически изменяя значение ячейки, вызывают правильное
что произойдет?
И если вы переместите вызов NotifyCurrentCellDirty после фактического изменения ячейки,
он тоже не ведет себя правильно. Все очень раздражает.
Ответ 2
Я нашел обходное решение на этой странице, хотя я не знаю, почему он работает
public MyForm()
{
InitializeComponent();
//Create a BindingSource, set its DataSource to my list,
//set the DataGrid DataSource to the BindindingSource...
_bindingSource.AddingNew += OnAddingNewToBindingSource;
}
private void OnAddingNewToBindingSource(object sender, AddingNewEventArgs e)
{
if(dataGridView1.Rows.Count == _bindingSource.Count)
{
_bindingSource.RemoveAt(_bindingSource.Count - 1);
}
}
Мне очень тяжело тратить столько времени на ошибки Visual Studio...
Ответ 3
Это старый, но я запускаю VS2010 и просто сталкиваюсь с этой проблемой. У меня есть DataGridView
, связанный с List<T>
с помощью BindingList<T>
. У меня есть событие drag n 'drop на моем DataGridView
, и оно выкинет это исключение после удаления всех строк из DGV (за исключением последнего пустого, который нельзя удалить), а затем добавление новых строк в DGV в DragDrop
через BindingList<T>
. Это исключение не было выбрано, если я просто добавил строки вручную, редактируя отдельные ячейки.
В одном из сообщений, которые я читал, обрабатывается событие BindingList<T>.AddNew
, но я обнаружил, что это событие не срабатывало при вызове BindingList<T>.Add()
в обработчике событий DragDrop
(я не уверен, почему). Я решил проблему, добавив
if(bindingList.Count == 0)
bindingList.RemoveAt(0)
внутри обработчика события DragDrop
перед добавлением новых объектов в bindingList
. Казалось, что добавление объекта к bindingList
завершилось неудачно, когда единственным "объектом" в bindingList
был тот, который связан с последней пустой строкой. Точка BindingList<T>
должна позволить разработчику работать с ней, а не непосредственно с DGV, но похоже, что это может вызвать проблемы в пограничных случаях.
Отношения между строками DGV и строками BindingList<T>
кажутся немного серыми. Я не проводил много времени, исследуя это, но мне непонятно, каково состояние "объекта" в BindingList<T>
, связанное с конечной (пустой) строкой DGV. Однако, похоже, что "объект" в конце создается только "правильно", когда вы напрямую взаимодействуете с последней строкой (а не через DataSource
).
Ответ 4
Попробуйте следующее:
if (openFileDialog1.ShowDialog() == DialogResult.OK)
{
int row = e.RowIndex;
int clmn = e.ColumnIndex;
if(e.RowIndex == dataGridView1.Rows.Count- 1)
dataGridView1.Rows.Add();
dataGridView1.Rows[row].Cells[clmn].Value = openFileDialog1.FileName;
}
ИЗМЕНИТЬ
Я не заметил, что вы привязываете свой datagridview:(
Хорошо, чтобы решить эту проблему: используйте источник привязки, установите его свойство DataSource в свой список, затем установите источник данных в виде сетки данных в этот источник привязки. Теперь код должен выглядеть так:
public partial class frmTestDataGridView : Form
{
BindingSource bindingSource1 = new BindingSource();
List<string> datasource = new List<string>();
public frmTestDataGridView()
{
InitializeComponent();
datasource.Add("item1");
datasource.Add("item2");
datasource.Add("item3");
bindingSource1.DataSource = datasource;
dataGridView1.DataSource = bindingSource1;
}
private void dataGridView1_CellClick(object sender, DataGridViewCellEventArgs e)
{
if (openFileDialog1.ShowDialog() == DialogResult.OK)
{
int row = e.RowIndex;
int clmn = e.ColumnIndex;
if (e.RowIndex == dataGridView1.Rows.Count - 1)
{
bindingSource1.Add("");
}
dataGridView1.Rows[row].Cells[clmn].Value = openFileDialog1.FileName;
}
}
}
Ответ 5
Не забудьте использовать Row.BeginEdit()
и Row.EndEdit()
, если вы получите эту ошибку при редактировании значения в строке, используя DataGrid или GridEX от Janus (в моем случае). Пример кода, который Даррель Ли опубликовал здесь (fooobar.com/info/338954/...), напоминает мне использовать эти инструкции, которые я забыл использовать, и это решило проблему для меня.