Как программно добавить новые строки в winforms datagridview в ответ на код запуска, введенный в текущую строку?

#c# #winforms #datagridview

#c# #winforms #datagridview

Вопрос:

У меня есть datagridview в приложении c # .net WinForms, которое фиксирует строки сведений о заказе. Когда оператор вводит код элемента, который является составным элементом, я хочу иметь возможность автоматически вставлять части этого элемента.

Я думал, что это будет легко, поскольку у меня есть функция импорта, с помощью которой можно анализировать электронную таблицу и вводить строки непосредственно в источник данных datagridview (строго типизированную таблицу). Когда это происходит, сетка обновляется сама по себе.

Однако я обнаружил, что когда оператор находится «в» строке, и я пытаюсь запустить это действие, новые данные заполняют строку новой строки «визуально», но новая новая строка не создается, и если я затем нажму на строку запуска, данные новой строки исчезнут. Таким образом, по сути, оператор больше не может ничего вводить после того, как они ввели код триггера, и строки детали в основном блокируются.

Я также пытался использовать BindingSources в качестве источника данных, но это не дало никакого эффекта.

Я думаю, что это потому, что код все еще находится в каком-то «событии», и это предотвращает добавление новых строк. Я попытался обработать событие RowEnter, чтобы затем проверить, был ли последний введенный код кодом сделки, но затем я возвращаюсь в середину события, и все это снова «блокируется». Итак, как выйти из пузыря событий, чтобы я мог добавлять новые строки?

Некоторые из моего кода ниже:

В подклассе datagridview: (это.Контроллер.BuildOrderLine(это.CurrentOrderLine);

         protected override void OnCellValueChanged(System.Windows.Forms.DataGridViewCellEventArgs e)
        {
            base.OnCellValueChanged(e);
            if ((this.Order == null) || (this.IsBinding) || (this.SelectedRows.Count == 0) || (e.RowIndex < 0)) { return; }
            this.LockCells(e.RowIndex, !this.IsCurrentOrderLineStubValid);
            this.ComputeOrderLine();
        }  

        protected void ComputeOrderLine()
        {
            if (this.IsReadOnlyGrid) { return; }

                  if (!this.SetCurrentOrderLine(this.CurrentCell.RowIndex)) { return; }
                if (this.CurrentCell.ColumnIndex == COLINDEX_LINEPRICE) { this.CurrentOrderLine.OverrideUnitPriceOn = true; }
                if (this.CurrentCell.ColumnIndex == COLINDEX_DISC)
                {
                    this.CurrentOrderLine.OverrideDiscountOn = true;
                    this.CurrentOrderLine.OverrideDiscountCommitted = false;
                    if (this.IsCurrentCellValueNullOrEmpty) { this.CurrentOrderLine.LineDiscountPct = 0; }
                }

                if (!this.CurrentOrderLine.IsLKeyNull())
                {
                    this.Controller.BuildOrderLine(this.CurrentOrderLine);

                    this.LockCells(this.CurrentRow.Index, this.CurrentOrderLine.IsParentItem);
                    this.HighlightRow(this.CurrentCell.RowIndex);
                }
        }
  

В контроллере:

 public void BuildOrderLine(DBilling.BillingDocumentDetailRow orderLine)
{
    SalesItem item = this.FindMaterialItem(orderLine.LKey);
    orderLine.LineSellingUnit = item.TriggerItem.SellingUnit;
    if (item.IsDealCode)
    {
        this.CurrentOrderLineItem = item;
        this.CurrentOrderLineRow = orderLine;
    }
     this.LineBuilder.ProjectItem(orderLine, item);        }
  

В объекте business helper:

 public virtual void ProjectItemDetails(DBilling.BillingDocumentDetailRow headerline, SalesItem item)
{
    if (!item.IsDealCode) { return; }
    DBilling.BillingDocumentDetailRow line = null;

    for (int i = 0; i < item.Parts.Count; i  )
    {
        line = this.DocumentDetail.NewBillingDocumentDetailRow();
        line.MaterialItemId = item.Parts[i].PartItem.PartItemId;
        line.LKey = item.Parts[i].BaseItem.LKey;
        line.MName = item.Parts[i].BaseItem.MName;
        line.ParentItemLineItemId = headerline.LineItemId;
        line.IsParentItem = false;
        line.LineCaptureMethod = "I";
        this.ProjectItemDetailLine(headerline.QuantitySold, ref line, item.Parts[i]);
        this.DocumentDetail.AddBillingDocumentDetailRow(line);
        this.ComputeLineTotals(line);
    }
}
  

Комментарии:

1. Что делает метод LockCells?

2. Привет, это делает определенные ячейки доступными только для чтения в соответствии с текущим состоянием строки.

Ответ №1:

Хорошо. итак, короткий ответ на мою проблему заключался в замене реализаций таблицы данных на BindingSource. В прошлом я сталкивался с парой других проблем, которые были решены простым использованием компонента BindingSource. В документации действительно говорится, что DataGridView разработан для очень тесной работы с BindingSources, и мой опыт подтверждает это.

Причина, по которой это работает, заключается в том, что DataGridView использует CurrencyManager внутренне для отслеживания положения строки. Хотя я полагал, что этот валютный менеджер обернет источник данных, который я также привязал к сетке, что он в некоторой степени и делает, похоже, что он не приостанавливает / не завершает редактирование текущей строки при использовании datatable в качестве источника данных.

Итак, я предполагаю, что это потому, что таблица не запускает ожидаемые события / возможно, не реализует соответствующие интерфейсы.

Подробнее:

Используя Redgate Reflector, я смог перейти к элементу управления DataGridView. DataGridView использует пару внутренних классов, к которым вы просто не можете получить доступ из своего собственного кода, наиболее важным из которых (для рассматриваемой проблемы) является:

 internal class DataGridViewDataConnection
  

В этом классе вызывается метод long:

 private void ProcessListChanged(ListChangedEventArgs e)
  

Важная часть этого метода заключается в следующем:

 if ((this.owner.NewRowIndex != -1) amp;amp; (e.NewIndex == this.owner.Rows.Count))
                {
                    throw new InvalidOperationException();
                }
                this.owner.Rows.InsertInternal(e.NewIndex, this.owner.RowTemplateClone, true);
            Label_05B4:
                if (((this.owner.Rows.Count > 0) amp;amp; !this.dataConnectionState[8]) amp;amp; !this.owner.InSortOperation)
                {
                    this.MatchCurrencyManagerPosition(false, e.ListChangedType == ListChangedType.Reset);
                }
            }
            finally
            {
                this.dataConnectionState[0x10] = false;
            }
  

Вы можете видеть, что это.Метод MatchCurrencyManagerPosition перемещает текущую позицию строки новой строки в конец таблицы, как показано ниже:

 public void MatchCurrencyManagerPosition(bool scrollIntoView, bool clearSelection)
{
    if (this.owner.Columns.Count != 0)
    {
        int columnIndex = (this.owner.CurrentCellAddress.X == -1) ? this.owner.FirstDisplayedColumnIndex : this.owner.CurrentCellAddress.X;
        if (columnIndex == -1)
        {
            DataGridViewColumn firstColumn = this.owner.Columns.GetFirstColumn(DataGridViewElementStates.None);
            firstColumn.Visible = true;
            columnIndex = firstColumn.Index;
        }
        int position = this.currencyManager.Position;
        if (position == -1)
        {
            if (!this.owner.SetCurrentCellAddressCore(-1, -1, false, false, false))
            {
                throw new InvalidOperationException(SR.GetString("DataGridView_CellChangeCannotBeCommittedOrAborted"));
            }
        }
        else if (position < this.owner.Rows.Count)
        {
            if ((this.owner.Rows.GetRowState(position) amp; DataGridViewElementStates.Visible) == DataGridViewElementStates.None)
            {
                this.owner.Rows[position].Visible = true;
            }
            if (((position != this.owner.CurrentCellAddress.Y) || (columnIndex != this.owner.CurrentCellAddress.X)) amp;amp; ((scrollIntoView amp;amp; !this.owner.ScrollIntoView(columnIndex, position, true)) || (((columnIndex < this.owner.Columns.Count) amp;amp; (position < this.owner.Rows.Count)) amp;amp; !this.owner.SetAndSelectCurrentCellAddress(columnIndex, position, true, false, false, clearSelection, false))))
            {
                throw new InvalidOperationException(SR.GetString("DataGridView_CellChangeCannotBeCommittedOrAborted"));
            }
        }
    }
}
  

Это была проблема, которую мне нужно было решить, поскольку строки, вводимые с помощью триггера, зависали в строке новой строки, и я просто не мог найти подходящее событие для завершения блокировки редактирования при использовании таблиц данных, и, как указано выше, эти методы были недоступны для моего кода.

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