mirror of
https://github.com/ajisaacs/OpenNest.git
synced 2026-10-06 19:42:10 -04:00
feat(ui): edit quantities in the BOM part table
The BOM import dialog's Parts table now binds to the BomPartRow objects themselves (a BindingList with Designer-defined columns) instead of a DataTable of strings: - Columns: Item #, File Name, Description, Material, Thickness, Qty, Status. Only Material, Thickness and Qty are editable, and only on rows that have a drawing; those rows are grey and locked. Rows that still need input are tinted amber. - Qty is editable. Anything but a whole number of 1 or more is refused in the cell with an error icon; Esc restores the old value. A blank-in-BOM quantity shows a tooltip saying 1 is used. - A thickness that is not a number above 0 is refused the same way; blanking it marks the row "Needs thickness". - Every edit goes through the row's bound part, so the Groups tab (part count and Total Qty) and the summary follow at once, and an edit can never land on a different part. Rows stay in BOM order: the headers no longer sort, which removes the sort-then-edit defect where index mapping applied an edit to another part. - Create Nests first finishes the cell being edited. BomImportFormTests (OpenNest.WinForms.Tests) cover the columns, the quantity edit and its group total, refused quantities, edits after the rows are reloaded in another order, locked rows and blanked material/thickness. They first run on the windows-desktop CI job; the sort defect's red evidence is source review, not an executed test. Project Memory: 5b0808d1-7353-4a34-87df-d1fbfc38f2b4
This commit is contained in:
1 parent
81d3253580
commit
124d70afe7
3 files changed
+408
-84
No files matched your search
@@ -0,0 +1,188 @@
|
||||
using System.Data;
|
||||
using System.Reflection;
|
||||
using System.Windows.Forms;
|
||||
using OpenNest.Forms;
|
||||
using OpenNest.IO.Bom;
|
||||
|
||||
namespace OpenNest.WinForms.Tests.Forms;
|
||||
|
||||
public class BomImportFormTests
|
||||
{
|
||||
[Fact]
|
||||
public void PartColumns_KeepBomOrderAndOnlyMaterialThicknessAndQtyAreEditable()
|
||||
{
|
||||
RunSta(() =>
|
||||
{
|
||||
using var form = Show(out var parts);
|
||||
|
||||
Assert.Equal(
|
||||
new[] { "Item #", "File Name", "Description", "Material", "Thickness", "Qty", "Status" },
|
||||
parts.Columns.Cast<DataGridViewColumn>().OrderBy(c => c.DisplayIndex).Select(c => c.HeaderText)
|
||||
);
|
||||
Assert.All(
|
||||
parts.Columns.Cast<DataGridViewColumn>(),
|
||||
c => Assert.Equal(DataGridViewColumnSortMode.NotSortable, c.SortMode)
|
||||
);
|
||||
Assert.Equal(
|
||||
new[] { "colMaterial", "colThickness", "colQty" },
|
||||
parts.Columns.Cast<DataGridViewColumn>().Where(c => !c.ReadOnly).Select(c => c.Name)
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void EditingQty_ChangesThatPartAndTheGroupTotal()
|
||||
{
|
||||
RunSta(() =>
|
||||
{
|
||||
using var form = Show(out var parts);
|
||||
var first = Ready("PT01", qty: 2);
|
||||
var second = Ready("PT02", qty: 3);
|
||||
form.LoadRows(new[] { first, second });
|
||||
Assert.Equal(5, GroupTotal(form, 0));
|
||||
|
||||
Assert.True(Edit(parts, 0, "colQty", "5"));
|
||||
|
||||
Assert.Equal(5, first.Qty);
|
||||
Assert.Equal(3, second.Qty);
|
||||
Assert.Equal(8, GroupTotal(form, 0));
|
||||
Assert.Equal("Ready", parts.Rows[0].Cells["colStatus"].Value);
|
||||
});
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("0")]
|
||||
[InlineData("-1")]
|
||||
[InlineData("2.5")]
|
||||
[InlineData("abc")]
|
||||
[InlineData("")]
|
||||
public void InvalidQty_IsRefusedAndTheOldValueKept(string text)
|
||||
{
|
||||
RunSta(() =>
|
||||
{
|
||||
using var form = Show(out var parts);
|
||||
var part = Ready("PT01", qty: 2);
|
||||
form.LoadRows(new[] { part });
|
||||
|
||||
Assert.False(Edit(parts, 0, "colQty", text));
|
||||
Assert.True(parts.IsCurrentCellInEditMode);
|
||||
Assert.False(string.IsNullOrEmpty(parts.CurrentCell.ErrorText));
|
||||
|
||||
parts.CancelEdit();
|
||||
Assert.True(parts.EndEdit());
|
||||
|
||||
Assert.Equal(2, part.Qty);
|
||||
Assert.Equal(2, GroupTotal(form, 0));
|
||||
});
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Edits_ChangeThePartShownInThatRow_AfterTheRowsAreReloadedInAnotherOrder()
|
||||
{
|
||||
RunSta(() =>
|
||||
{
|
||||
using var form = Show(out var parts);
|
||||
var a = Ready("PT01", qty: 1);
|
||||
var b = Ready("PT02", qty: 1);
|
||||
var c = Ready("PT03", qty: 1);
|
||||
form.LoadRows(new[] { a, b, c });
|
||||
|
||||
Assert.True(Edit(parts, 2, "colMaterial", "Aluminum"));
|
||||
Assert.Equal("Aluminum", c.Material);
|
||||
Assert.Equal("Stainless", a.Material);
|
||||
|
||||
form.LoadRows(new[] { c, a, b });
|
||||
Assert.Same(c, parts.Rows[0].DataBoundItem);
|
||||
|
||||
Assert.True(Edit(parts, 0, "colQty", "9"));
|
||||
Assert.Equal(9, c.Qty);
|
||||
Assert.Equal(1, a.Qty);
|
||||
});
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void RowsWithoutADrawing_CannotEnterEditMode()
|
||||
{
|
||||
RunSta(() =>
|
||||
{
|
||||
using var form = Show(out var parts);
|
||||
var noDrawing = Ready("PT01", qty: 1);
|
||||
noDrawing.DxfPath = null;
|
||||
form.LoadRows(new[] { noDrawing, Ready("PT02", qty: 1) });
|
||||
|
||||
parts.CurrentCell = parts.Rows[0].Cells["colQty"];
|
||||
Assert.False(parts.BeginEdit(selectAll: true));
|
||||
Assert.False(parts.IsCurrentCellInEditMode);
|
||||
|
||||
parts.CurrentCell = parts.Rows[1].Cells["colQty"];
|
||||
Assert.True(parts.BeginEdit(selectAll: true));
|
||||
parts.CancelEdit();
|
||||
});
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void BlankingMaterialOrThickness_MarksTheRowAndDropsItFromTheGroups()
|
||||
{
|
||||
RunSta(() =>
|
||||
{
|
||||
using var form = Show(out var parts);
|
||||
var first = Ready("PT01", qty: 1);
|
||||
var second = Ready("PT02", qty: 1);
|
||||
form.LoadRows(new[] { first, second });
|
||||
|
||||
Assert.True(Edit(parts, 0, "colMaterial", " "));
|
||||
Assert.Null(first.Material);
|
||||
Assert.Equal("Needs material", parts.Rows[0].Cells["colStatus"].Value);
|
||||
|
||||
Assert.False(Edit(parts, 1, "colThickness", "abc"));
|
||||
parts.CancelEdit();
|
||||
Assert.True(parts.EndEdit());
|
||||
Assert.Equal(0.25, second.Thickness);
|
||||
|
||||
Assert.True(Edit(parts, 1, "colThickness", ""));
|
||||
Assert.Null(second.Thickness);
|
||||
Assert.Equal("Needs thickness", parts.Rows[1].Cells["colStatus"].Value);
|
||||
|
||||
Assert.Empty(GroupsTable(form).Rows);
|
||||
Assert.Equal("0 ready, 1 needs a material, 1 needs a thickness", Field<Label>(form, "lblSummary").Text);
|
||||
});
|
||||
}
|
||||
|
||||
private static BomImportForm Show(out DataGridView parts)
|
||||
{
|
||||
var form = new BomImportForm();
|
||||
form.Show();
|
||||
parts = Field<DataGridView>(form, "dgvParts");
|
||||
return form;
|
||||
}
|
||||
|
||||
private static bool Edit(DataGridView grid, int row, string column, string text)
|
||||
{
|
||||
grid.CurrentCell = grid.Rows[row].Cells[column];
|
||||
Assert.True(grid.BeginEdit(selectAll: true));
|
||||
grid.EditingControl!.Text = text;
|
||||
return grid.EndEdit();
|
||||
}
|
||||
|
||||
private static DataTable GroupsTable(BomImportForm form) =>
|
||||
Assert.IsType<DataTable>(Field<DataGridView>(form, "dgvGroups").DataSource);
|
||||
|
||||
private static int GroupTotal(BomImportForm form, int group) =>
|
||||
(int)GroupsTable(form).Rows[group]["Total Qty"];
|
||||
|
||||
private static BomPartRow Ready(string fileName, int qty) =>
|
||||
new()
|
||||
{
|
||||
FileName = fileName,
|
||||
DxfPath = $@"C:\drawings\{fileName}.dxf",
|
||||
Material = "Stainless",
|
||||
Thickness = 0.25,
|
||||
Qty = qty,
|
||||
};
|
||||
|
||||
private static T Field<T>(object owner, string name) =>
|
||||
(T)owner.GetType().GetField(name, BindingFlags.Instance | BindingFlags.NonPublic)!.GetValue(owner)!;
|
||||
|
||||
private static void RunSta(Action action) =>
|
||||
StaTestThread.Run(action, TimeSpan.FromSeconds(30), "The BOM import dialog test did not complete.");
|
||||
}
|
||||
Generated
+89
@@ -15,6 +15,8 @@ namespace OpenNest.Forms
|
||||
|
||||
private void InitializeComponent()
|
||||
{
|
||||
System.Windows.Forms.DataGridViewCellStyle thicknessCellStyle = new System.Windows.Forms.DataGridViewCellStyle();
|
||||
System.Windows.Forms.DataGridViewCellStyle qtyCellStyle = new System.Windows.Forms.DataGridViewCellStyle();
|
||||
grpInput = new System.Windows.Forms.GroupBox();
|
||||
tbl = new System.Windows.Forms.TableLayoutPanel();
|
||||
lblJobName = new System.Windows.Forms.Label();
|
||||
@@ -34,6 +36,13 @@ namespace OpenNest.Forms
|
||||
tabControl = new System.Windows.Forms.TabControl();
|
||||
tabParts = new System.Windows.Forms.TabPage();
|
||||
dgvParts = new System.Windows.Forms.DataGridView();
|
||||
colItemNum = new System.Windows.Forms.DataGridViewTextBoxColumn();
|
||||
colFileName = new System.Windows.Forms.DataGridViewTextBoxColumn();
|
||||
colDescription = new System.Windows.Forms.DataGridViewTextBoxColumn();
|
||||
colMaterial = new System.Windows.Forms.DataGridViewTextBoxColumn();
|
||||
colThickness = new System.Windows.Forms.DataGridViewTextBoxColumn();
|
||||
colQty = new System.Windows.Forms.DataGridViewTextBoxColumn();
|
||||
colStatus = new System.Windows.Forms.DataGridViewTextBoxColumn();
|
||||
tabGroups = new System.Windows.Forms.TabPage();
|
||||
dgvGroups = new System.Windows.Forms.DataGridView();
|
||||
pnlBottom = new System.Windows.Forms.Panel();
|
||||
@@ -265,11 +274,84 @@ namespace OpenNest.Forms
|
||||
dgvParts.BackgroundColor = System.Drawing.SystemColors.Window;
|
||||
dgvParts.BorderStyle = System.Windows.Forms.BorderStyle.None;
|
||||
dgvParts.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize;
|
||||
dgvParts.Columns.AddRange(new System.Windows.Forms.DataGridViewColumn[] { colItemNum, colFileName, colDescription, colMaterial, colThickness, colQty, colStatus });
|
||||
dgvParts.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||
dgvParts.Name = "dgvParts";
|
||||
dgvParts.RowHeadersVisible = false;
|
||||
dgvParts.SelectionMode = System.Windows.Forms.DataGridViewSelectionMode.FullRowSelect;
|
||||
dgvParts.TabIndex = 0;
|
||||
dgvParts.CellBeginEdit += DgvParts_CellBeginEdit;
|
||||
dgvParts.CellEndEdit += DgvParts_CellEndEdit;
|
||||
dgvParts.CellFormatting += DgvParts_CellFormatting;
|
||||
dgvParts.CellParsing += DgvParts_CellParsing;
|
||||
dgvParts.CellToolTipTextNeeded += DgvParts_CellToolTipTextNeeded;
|
||||
dgvParts.CellValidating += DgvParts_CellValidating;
|
||||
dgvParts.DataError += DgvParts_DataError;
|
||||
//
|
||||
// colItemNum
|
||||
//
|
||||
colItemNum.DataPropertyName = "ItemNum";
|
||||
colItemNum.FillWeight = 40F;
|
||||
colItemNum.HeaderText = "Item #";
|
||||
colItemNum.Name = "colItemNum";
|
||||
colItemNum.ReadOnly = true;
|
||||
colItemNum.SortMode = System.Windows.Forms.DataGridViewColumnSortMode.NotSortable;
|
||||
//
|
||||
// colFileName
|
||||
//
|
||||
colFileName.DataPropertyName = "FileName";
|
||||
colFileName.FillWeight = 120F;
|
||||
colFileName.HeaderText = "File Name";
|
||||
colFileName.Name = "colFileName";
|
||||
colFileName.ReadOnly = true;
|
||||
colFileName.SortMode = System.Windows.Forms.DataGridViewColumnSortMode.NotSortable;
|
||||
//
|
||||
// colDescription
|
||||
//
|
||||
colDescription.DataPropertyName = "Description";
|
||||
colDescription.FillWeight = 150F;
|
||||
colDescription.HeaderText = "Description";
|
||||
colDescription.Name = "colDescription";
|
||||
colDescription.ReadOnly = true;
|
||||
colDescription.SortMode = System.Windows.Forms.DataGridViewColumnSortMode.NotSortable;
|
||||
//
|
||||
// colMaterial
|
||||
//
|
||||
colMaterial.DataPropertyName = "Material";
|
||||
colMaterial.FillWeight = 110F;
|
||||
colMaterial.HeaderText = "Material";
|
||||
colMaterial.Name = "colMaterial";
|
||||
colMaterial.SortMode = System.Windows.Forms.DataGridViewColumnSortMode.NotSortable;
|
||||
//
|
||||
// colThickness
|
||||
//
|
||||
thicknessCellStyle.Alignment = System.Windows.Forms.DataGridViewContentAlignment.MiddleRight;
|
||||
thicknessCellStyle.Format = "0.####";
|
||||
colThickness.DataPropertyName = "Thickness";
|
||||
colThickness.DefaultCellStyle = thicknessCellStyle;
|
||||
colThickness.FillWeight = 70F;
|
||||
colThickness.HeaderText = "Thickness";
|
||||
colThickness.Name = "colThickness";
|
||||
colThickness.SortMode = System.Windows.Forms.DataGridViewColumnSortMode.NotSortable;
|
||||
//
|
||||
// colQty
|
||||
//
|
||||
qtyCellStyle.Alignment = System.Windows.Forms.DataGridViewContentAlignment.MiddleRight;
|
||||
colQty.DataPropertyName = "Qty";
|
||||
colQty.DefaultCellStyle = qtyCellStyle;
|
||||
colQty.FillWeight = 50F;
|
||||
colQty.HeaderText = "Qty";
|
||||
colQty.Name = "colQty";
|
||||
colQty.SortMode = System.Windows.Forms.DataGridViewColumnSortMode.NotSortable;
|
||||
//
|
||||
// colStatus
|
||||
//
|
||||
colStatus.DataPropertyName = "StatusText";
|
||||
colStatus.FillWeight = 100F;
|
||||
colStatus.HeaderText = "Status";
|
||||
colStatus.Name = "colStatus";
|
||||
colStatus.ReadOnly = true;
|
||||
colStatus.SortMode = System.Windows.Forms.DataGridViewColumnSortMode.NotSortable;
|
||||
//
|
||||
// tabGroups
|
||||
//
|
||||
@@ -383,6 +465,13 @@ namespace OpenNest.Forms
|
||||
private System.Windows.Forms.TabControl tabControl;
|
||||
private System.Windows.Forms.TabPage tabParts;
|
||||
private System.Windows.Forms.DataGridView dgvParts;
|
||||
private System.Windows.Forms.DataGridViewTextBoxColumn colItemNum;
|
||||
private System.Windows.Forms.DataGridViewTextBoxColumn colFileName;
|
||||
private System.Windows.Forms.DataGridViewTextBoxColumn colDescription;
|
||||
private System.Windows.Forms.DataGridViewTextBoxColumn colMaterial;
|
||||
private System.Windows.Forms.DataGridViewTextBoxColumn colThickness;
|
||||
private System.Windows.Forms.DataGridViewTextBoxColumn colQty;
|
||||
private System.Windows.Forms.DataGridViewTextBoxColumn colStatus;
|
||||
private System.Windows.Forms.TabPage tabGroups;
|
||||
private System.Windows.Forms.DataGridView dgvGroups;
|
||||
private System.Windows.Forms.Panel pnlBottom;
|
||||
|
||||
+131
-84
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Data;
|
||||
using System.Drawing;
|
||||
using System.IO;
|
||||
@@ -12,9 +13,10 @@ namespace OpenNest.Forms
|
||||
{
|
||||
public partial class BomImportForm : Form
|
||||
{
|
||||
private List<BomPartRow> _parts;
|
||||
private static readonly Color NeedsInputBackColor = Color.FromArgb(255, 240, 200);
|
||||
|
||||
private readonly BindingList<BomPartRow> _parts;
|
||||
private Dictionary<string, BomGroupPlateSettings> _groupSettings;
|
||||
private bool _suppressRegroup;
|
||||
private NestDefaults _defaults;
|
||||
|
||||
public Form MdiParentForm { get; set; }
|
||||
@@ -22,7 +24,10 @@ namespace OpenNest.Forms
|
||||
public BomImportForm()
|
||||
{
|
||||
InitializeComponent();
|
||||
_parts = new List<BomPartRow>();
|
||||
_parts = new BindingList<BomPartRow>();
|
||||
_parts.ListChanged += Parts_ListChanged;
|
||||
dgvParts.AutoGenerateColumns = false;
|
||||
dgvParts.DataSource = _parts;
|
||||
_groupSettings = new Dictionary<string, BomGroupPlateSettings>();
|
||||
_defaults = MainForm.LoadSavedNestDefaults(out _);
|
||||
ApplyDefaults();
|
||||
@@ -99,11 +104,7 @@ namespace OpenNest.Forms
|
||||
using (var reader = new BomReader(txtBomFile.Text))
|
||||
items = reader.GetItems();
|
||||
|
||||
_parts = BomImportRows.Build(items, txtDxfFolder.Text);
|
||||
_groupSettings.Clear();
|
||||
PopulatePartsGrid();
|
||||
RebuildGroups();
|
||||
UpdateSummary();
|
||||
LoadRows(BomImportRows.Build(items, txtDxfFolder.Text));
|
||||
btnCreateNests.Enabled = true;
|
||||
tabControl.SelectedTab = tabParts;
|
||||
}
|
||||
@@ -122,87 +123,133 @@ namespace OpenNest.Forms
|
||||
|
||||
#region Parts Tab
|
||||
|
||||
private void PopulatePartsGrid()
|
||||
/// <summary>
|
||||
/// Shows <paramref name="rows"/> in the Parts table, in their order, and
|
||||
/// rebuilds the groups and summary from them.
|
||||
/// </summary>
|
||||
internal void LoadRows(IEnumerable<BomPartRow> rows)
|
||||
{
|
||||
_suppressRegroup = true;
|
||||
|
||||
var table = new DataTable();
|
||||
table.Columns.Add("Item #", typeof(string));
|
||||
table.Columns.Add("File Name", typeof(string));
|
||||
table.Columns.Add("Qty", typeof(string));
|
||||
table.Columns.Add("Description", typeof(string));
|
||||
table.Columns.Add("Material", typeof(string));
|
||||
table.Columns.Add("Thickness", typeof(string));
|
||||
table.Columns.Add("Status", typeof(string));
|
||||
|
||||
foreach (var part in _parts)
|
||||
{
|
||||
table.Rows.Add(
|
||||
part.ItemNum?.ToString() ?? "",
|
||||
part.FileName ?? "",
|
||||
part.Qty?.ToString() ?? "",
|
||||
part.Description ?? "",
|
||||
part.Material ?? "",
|
||||
part.Thickness?.ToString("0.####") ?? "",
|
||||
part.StatusText
|
||||
);
|
||||
}
|
||||
|
||||
dgvParts.DataSource = table;
|
||||
|
||||
// Make non-editable columns read-only
|
||||
foreach (DataGridViewColumn col in dgvParts.Columns)
|
||||
{
|
||||
if (col.Name != "Material" && col.Name != "Thickness")
|
||||
col.ReadOnly = true;
|
||||
}
|
||||
|
||||
// Style rows by status
|
||||
for (var i = 0; i < _parts.Count; i++)
|
||||
{
|
||||
if (!_parts[i].IsEditable)
|
||||
{
|
||||
dgvParts.Rows[i].ReadOnly = true;
|
||||
dgvParts.Rows[i].DefaultCellStyle.ForeColor = Color.Gray;
|
||||
}
|
||||
}
|
||||
|
||||
dgvParts.CellValueChanged -= DgvParts_CellValueChanged;
|
||||
dgvParts.CellValueChanged += DgvParts_CellValueChanged;
|
||||
|
||||
_suppressRegroup = false;
|
||||
}
|
||||
|
||||
private void DgvParts_CellValueChanged(object sender, DataGridViewCellEventArgs e)
|
||||
{
|
||||
if (_suppressRegroup || e.RowIndex < 0)
|
||||
return;
|
||||
|
||||
var colName = dgvParts.Columns[e.ColumnIndex].Name;
|
||||
if (colName != "Material" && colName != "Thickness")
|
||||
return;
|
||||
|
||||
var part = _parts[e.RowIndex];
|
||||
if (!part.IsEditable)
|
||||
return;
|
||||
|
||||
if (colName == "Material")
|
||||
part.Material = dgvParts.Rows[e.RowIndex].Cells[e.ColumnIndex].Value?.ToString();
|
||||
|
||||
if (colName == "Thickness")
|
||||
{
|
||||
var text = dgvParts.Rows[e.RowIndex].Cells[e.ColumnIndex].Value?.ToString();
|
||||
part.Thickness = double.TryParse(text, out var t) ? t : (double?)null;
|
||||
}
|
||||
|
||||
_suppressRegroup = true;
|
||||
dgvParts.Rows[e.RowIndex].Cells["Status"].Value = part.StatusText;
|
||||
_suppressRegroup = false;
|
||||
_groupSettings.Clear();
|
||||
_parts.RaiseListChangedEvents = false;
|
||||
_parts.Clear();
|
||||
foreach (var row in rows)
|
||||
_parts.Add(row);
|
||||
_parts.RaiseListChangedEvents = true;
|
||||
_parts.ResetBindings();
|
||||
|
||||
RebuildGroups();
|
||||
UpdateSummary();
|
||||
}
|
||||
|
||||
private BomPartRow PartAt(int rowIndex) =>
|
||||
rowIndex >= 0 && rowIndex < dgvParts.Rows.Count
|
||||
? dgvParts.Rows[rowIndex].DataBoundItem as BomPartRow
|
||||
: null;
|
||||
|
||||
private void Parts_ListChanged(object sender, ListChangedEventArgs e)
|
||||
{
|
||||
if (e.ListChangedType != ListChangedType.ItemChanged)
|
||||
return;
|
||||
|
||||
switch (e.PropertyDescriptor?.Name)
|
||||
{
|
||||
case nameof(BomPartRow.Material):
|
||||
case nameof(BomPartRow.Thickness):
|
||||
case nameof(BomPartRow.Qty):
|
||||
RebuildGroups();
|
||||
UpdateSummary();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void DgvParts_CellBeginEdit(object sender, DataGridViewCellCancelEventArgs e)
|
||||
{
|
||||
// Rows without a drawing can never be imported, so they stay locked.
|
||||
if (PartAt(e.RowIndex) is not { IsEditable: true })
|
||||
e.Cancel = true;
|
||||
}
|
||||
|
||||
private void DgvParts_CellValidating(object sender, DataGridViewCellValidatingEventArgs e)
|
||||
{
|
||||
if (!dgvParts.IsCurrentCellInEditMode)
|
||||
return;
|
||||
|
||||
var text = e.FormattedValue?.ToString();
|
||||
string error = null;
|
||||
|
||||
if (e.ColumnIndex == colQty.Index && !BomQuantity.TryParse(text, out _))
|
||||
error = "Enter a whole number of 1 or more.";
|
||||
else if (
|
||||
e.ColumnIndex == colThickness.Index
|
||||
&& !string.IsNullOrWhiteSpace(text)
|
||||
&& !TryParseThickness(text, out _)
|
||||
)
|
||||
error = "Enter a thickness greater than 0, or leave it blank.";
|
||||
|
||||
dgvParts.Rows[e.RowIndex].Cells[e.ColumnIndex].ErrorText = error ?? "";
|
||||
if (error != null)
|
||||
e.Cancel = true;
|
||||
}
|
||||
|
||||
private void DgvParts_CellParsing(object sender, DataGridViewCellParsingEventArgs e)
|
||||
{
|
||||
var text = e.Value?.ToString();
|
||||
|
||||
if (e.ColumnIndex == colQty.Index && BomQuantity.TryParse(text, out var qty))
|
||||
{
|
||||
e.Value = qty;
|
||||
e.ParsingApplied = true;
|
||||
}
|
||||
else if (e.ColumnIndex == colThickness.Index)
|
||||
{
|
||||
e.Value = TryParseThickness(text, out var thickness) ? thickness : null;
|
||||
e.ParsingApplied = true;
|
||||
}
|
||||
else if (e.ColumnIndex == colMaterial.Index)
|
||||
{
|
||||
e.Value = string.IsNullOrWhiteSpace(text) ? null : text;
|
||||
e.ParsingApplied = true;
|
||||
}
|
||||
}
|
||||
|
||||
private void DgvParts_CellEndEdit(object sender, DataGridViewCellEventArgs e)
|
||||
{
|
||||
dgvParts.Rows[e.RowIndex].Cells[e.ColumnIndex].ErrorText = "";
|
||||
}
|
||||
|
||||
private void DgvParts_CellFormatting(object sender, DataGridViewCellFormattingEventArgs e)
|
||||
{
|
||||
var part = PartAt(e.RowIndex);
|
||||
if (part == null)
|
||||
return;
|
||||
|
||||
if (!part.IsEditable)
|
||||
e.CellStyle.ForeColor = Color.Gray;
|
||||
else if (part.Status != BomRowStatus.Ready)
|
||||
e.CellStyle.BackColor = NeedsInputBackColor;
|
||||
}
|
||||
|
||||
private void DgvParts_CellToolTipTextNeeded(
|
||||
object sender,
|
||||
DataGridViewCellToolTipTextNeededEventArgs e
|
||||
)
|
||||
{
|
||||
if (e.ColumnIndex == colQty.Index && PartAt(e.RowIndex) is { QtyAssumed: true })
|
||||
e.ToolTipText = "The BOM has no quantity for this part; 1 is used.";
|
||||
}
|
||||
|
||||
private void DgvParts_DataError(object sender, DataGridViewDataErrorEventArgs e)
|
||||
{
|
||||
// Never show the grid's default error dialog; keep the edit open instead.
|
||||
e.ThrowException = false;
|
||||
e.Cancel = true;
|
||||
if (e.RowIndex >= 0 && e.ColumnIndex >= 0)
|
||||
dgvParts.Rows[e.RowIndex].Cells[e.ColumnIndex].ErrorText = "This value cannot be used.";
|
||||
}
|
||||
|
||||
private static bool TryParseThickness(string text, out double thickness) =>
|
||||
double.TryParse(text, out thickness) && double.IsFinite(thickness) && thickness > 0;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Groups Tab
|
||||
@@ -319,7 +366,7 @@ namespace OpenNest.Forms
|
||||
|
||||
private void CreateNests_Click(object sender, EventArgs e)
|
||||
{
|
||||
if (_parts == null || _parts.Count == 0)
|
||||
if (_parts.Count == 0 || !dgvParts.EndEdit())
|
||||
return;
|
||||
|
||||
// Save latest group edits
|
||||
|
||||
Reference in new issue
Block a user