Индекс-1 не имеет значения

Я получаю странную ошибку, о которой я абсолютно не понимаю. Я отправлю описание вместе с некоторым кодом здесь, и, надеюсь, один из вас, ребята, может указать мне в правильном направлении.

My app (Winforms) позволяет пользователю добавлять элементы в datagridview (привязанные к списку), и каждый раз, когда элемент добавляется, список сериализуется в XML файл. Когда приложение изначально запускается, программа проверяет файл xml и, если найден, добавляет ранее добавленные элементы в dgv.

Ive также добавил DataGridViewButtonColumn для удаления элементов из dgv (list). Вот некоторые из кода.

Основной класс:

 static void Main()
        {
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            Application.Run(new formFoldingClient());
        }

Конструктор формы вызывает этот метод для первоначальной настройки dgv

private void InitialDataGridViewSetup()
        {
            dgvClients.DataSource = null;

            //adding delete button column
            DataGridViewButtonColumn btnDelete = new DataGridViewButtonColumn();
            btnDelete.Name = "btnDelete";
            btnDelete.Text = "Delete";
            btnDelete.HeaderText = "Delete";
            btnDelete.UseColumnTextForButtonValue = true;
            btnDelete.DefaultCellStyle.BackColor = Color.DarkBlue;
            btnDelete.DefaultCellStyle.ForeColor = Color.White;
            dgvClients.Columns.Add(btnDelete);

            RefreshDataGridView();
        }

Каждый раз, когда элемент добавляется или удаляется, dgv обновляется, вызывая этот метод:

 private void RefreshDataGridView()
            {
                dgvClients.DataSource = null;

                if (clientList.Count != 0)
                {
                    dgvClients.DataSource = clientList;
                    dgvClients.Show();
                    dgvClients.ClearSelection();


                }
            }

Method that gets triggered when Delete button on a row in the dgv is pressed, followed by the method the performs the delete

 private void dgvClients_CellClick(object sender, DataGridViewCellEventArgs e)
        {
            if (e.ColumnIndex == 0) //delete button has been clicked
            {
                DeleteClient(dgvClients.Rows[e.RowIndex].Cells[e.ColumnIndex + 1].FormattedValue.ToString());
            }
        }

        private void DeleteClient(string clientToDelete)
        {
            dgvGrid.DataSource = null;
            int removeAt = new int();

            for (int i=0; i<clientList.Count; i++)
            {
                if (clientList[i]._ClientName == clientToDelete)
                {
                    removeAt = i;
                    break;
                }
            }

            clientList.RemoveAt(removeAt);
            LogToFile("Removed client: " + clientToDelete);
            LogToBox("Removed client: " + clientToDelete);
            RefreshDataGridView();
            SaveConfigAsXml();
            LogToFile("Changes after deletion persisted to clients.xml.");

        }

Я считаю, что это все код, который должен быть обязательным. Если вам нужно больше, дайте мне знать.

Краткое описание проблемы Когда приложение сначала загружается, если находит xml и загружает эти элементы в список, все выполняется так, как ожидалось. Я могу добавить больше элементов, удалить все элементы (по одному) и т.д.

Однако, если я начинаю без начального xml, добавление элементов не является проблемой. Но когда я удаляю последний оставшийся элемент в dgv, я получаю следующее исключение в последней строке Main()

Index out of range Exception: {"Index -1 does not have a value."}

Трассировка стека

at System.Windows.Forms.CurrencyManager.get_Item(Int32 index)
   at System.Windows.Forms.CurrencyManager.get_Current()
   at System.Windows.Forms.DataGridView.DataGridViewDataConnection.OnRowEnter(DataGridViewCellEventArgs e)
   at System.Windows.Forms.DataGridView.OnRowEnter(DataGridViewCell& dataGridViewCell, Int32 columnIndex, Int32 rowIndex, Boolean canCreateNewRow, Boolean validationFailureOccurred)
   at System.Windows.Forms.DataGridView.SetCurrentCellAddressCore(Int32 columnIndex, Int32 rowIndex, Boolean setAnchorCellAddress, Boolean validateCurrentCell, Boolean throughMouseClick)
   at System.Windows.Forms.DataGridView.OnCellMouseDown(HitTestInfo hti, Boolean isShiftDown, Boolean isControlDown)
   at System.Windows.Forms.DataGridView.OnCellMouseDown(DataGridViewCellMouseEventArgs e)
   at System.Windows.Forms.DataGridView.OnMouseDown(MouseEventArgs e)
   at System.Windows.Forms.Control.WmMouseDown(Message& m, MouseButtons button, Int32 clicks)
   at System.Windows.Forms.Control.WndProc(Message& m)
   at System.Windows.Forms.DataGridView.WndProc(Message& m)
   at System.Windows.Forms.Control.ControlNativeWindow.OnMessage(Message& m)
   at System.Windows.Forms.Control.ControlNativeWindow.WndProc(Message& m)
   at System.Windows.Forms.NativeWindow.DebuggableCallback(IntPtr hWnd, Int32 msg, IntPtr wparam, IntPtr lparam)
   at System.Windows.Forms.UnsafeNativeMethods.DispatchMessageW(MSG& msg)
   at System.Windows.Forms.Application.ComponentManager.System.Windows.Forms.UnsafeNativeMethods.IMsoComponentManager.FPushMessageLoop(IntPtr dwComponentID, Int32 reason, Int32 pvLoopData)
   at System.Windows.Forms.Application.ThreadContext.RunMessageLoopInner(Int32 reason, ApplicationContext context)
   at System.Windows.Forms.Application.ThreadContext.RunMessageLoop(Int32 reason, ApplicationContext context)
   at System.Windows.Forms.Application.Run(Form mainForm)
   at FoldingMonitorLocalClient.Program.Main() in C:\Users\xbonez\Documents\Visual Studio 2010\Projects\FoldingClient\FoldingClient\Program.cs:line 17
   at System.AppDomain._nExecuteAssembly(RuntimeAssembly assembly, String[] args)
   at System.AppDomain.ExecuteAssembly(String assemblyFile, Evidence assemblySecurity, String[] args)
   at Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly()
   at System.Threading.ThreadHelper.ThreadStart_Context(Object state)
   at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean ignoreSyncCtx)
   at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
   at System.Threading.ThreadHelper.ThreadStart()

Дополнительная информация Итак, я просто понял, что если у меня есть n элементов в dgv, удаление только первого элемента также вызывает одно и то же исключение. Удаление элементов с 2 по n не является проблемой.

Код, который читает xml и добавляет в список

 private void ReadFromConfigFile()
        {
            LogToFile("Beginning to read from clients.xml.");

            XmlSerializer deserializer = new XmlSerializer(typeof(List<Client>));

            try
            {
                List<Client> tempClientList = new List<Client>();
                using (Stream reader = new FileStream("clients.xml", FileMode.Open))
                {
                    tempClientList = ((List<Client>)deserializer.Deserialize(reader));
                }

                foreach (Client client in tempClientList)
                {
                    clientList.Add(client);
                }
            }
            catch (FileNotFoundException ex)
            {
                //config file does not exist
                this.LogToBox("No saved settings found.");
                this.LogToFile("No existing clients.xml present.", ex);
            }
            catch (Exception ex)
            {
                LogToBox("Unable to load saved settings. Please see log for more details.");
                LogToFile("Failed to read clients.xml.", ex);
            }
            finally
            {
                LogToFile("Finished reading clients.xml.");
            }
        }

Код при нажатии кнопки добавления

private void btnAdd_Click(object sender, EventArgs e)
        {
            this.tbxClientName.BackColor = Color.White;
            this.tbxLogLoc.BackColor = Color.White;

            bool exists = false;

            foreach (Client client in clientList)
            {
                if (client._ClientName == this.tbxClientName.Text)
                    exists = true;
            }

            if (String.IsNullOrEmpty(tbxClientName.Text))
            {
                this.tbxClientName.BackColor = Color.Yellow;
                LogToBox("Enter Client Name");
                LogToFile("user attempted to add client without specifying client name.");
            }
            else if (String.IsNullOrEmpty(tbxLogLoc.Text))
            {
                this.tbxLogLoc.BackColor = Color.Yellow;
                LogToBox("Select WorkLog location.");
                LogToFile("User attempted to add client without specifying worklog location.");
            }
            else if (exists)
            {
                //client name entered by user already exists
                LogToBox("Client name " + this.tbxClientName.Text + " already exists. Enter another Client name.");
                LogToFile("Duplicate client name entered.");
                this.tbxClientName.BackColor = Color.Yellow;
            }
            else
            {
                //everything is valid. Add new client to list
                clientList.Add(new Client(tbxClientName.Text, tbxLogLoc.Text));
                LogToBox("Added new client: " + tbxClientName.Text + ".");
                LogToFile("Added new client: " + tbxClientName.Text + ".");

                this.tbxClientName.Text = String.Empty;
                this.tbxLogLoc.Text = String.Empty;

                RefreshDataGridView();
                SaveConfigAsXml();
            }            
        }

Ответ 1

Обновление

Измените метод dgvClients_CellClick, чтобы включить дополнительные проверки:

 if (e.ColumnIndex == 0) //delete button has been clicked
            {
                if (e.RowIndex >= 0)
                {
                    DataGridViewRow dataGridViewRow = dataGridView1.Rows[e.RowIndex];

                    if (dataGridViewRow.Cells.Count > 1)
                    {
                        DeleteClient(dataGridViewRow.Cells[e.ColumnIndex + 1].FormattedValue.ToString());
                    }
                }
                else
                {
                    LogToFile(e.RowIndex.ToString());
                }
            }

Вы можете изменить проверку dgvClients_CellClick, чтобы включить e.RowIndex > 0, который должен предотвратить исключение. Помимо этого, чтобы узнать точную причину поведения, мы должны были бы взглянуть на логику добавить элемент и также быть клиентом.

Может быть, вам нужно установить выбранный индекс строки после добавления элементов вручную.

Ответ 2

Кажется, это некоторая внутренняя ошибка привязки в .NET. Я испытывал точно такое же исключение всякий раз, когда DataGridView привязан к списку. Я действительно потратил много времени, пытаясь найти решение, и мне наконец удалось избавиться от этих исключений сегодня - благодаря добавлению интерфейса ICurrencyManagerProvider ко всем моим спискам. Этот интерфейс имеет только свойство "CurrencyManager" для чтения и метод GetRelatedCurrencyManager. Я просто возвращаю Nothing в обоих из них, и что это больше не имеет значения CurrencyManager "index -1 не имеет ценности".

EDIT: ОК, только что выяснили, что "правильный путь" на самом деле должен использовать класс BindingList (of T) вместо List (of T)