#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 используется менеджер валют, я решил, что попробую замену источника привязки для таблиц данных, которая решила проблему.