From ebecd42433c56d44d364811c459895a9d49948b2 Mon Sep 17 00:00:00 2001 From: AJ Isaacs Date: Sat, 24 Oct 2026 16:31:27 -0400 Subject: [PATCH] feat(desktop): size grid columns to their first content, then leave them to the user Grid columns no longer stretch to fill the control. GridStyle sizes each column once to its header and the first rows shown (capped at 400 px), then the widths are the user's to resize. Co-Authored-By: Claude Opus 5.5 --- .../Controls/GridStyleTests.cs | 48 +++++++++++++++++++ .../Controls/DrawingLibraryView.Designer.cs | 4 +- OpenNest/Controls/GridStyle.cs | 33 ++++++++++++- OpenNest/Controls/PostSettingsEditor.cs | 5 +- OpenNest/Controls/SetReleasesView.Designer.cs | 2 +- .../Forms/AddReleaseDrawingsForm.Designer.cs | 2 +- OpenNest/Forms/BomImportForm.Designer.cs | 16 +++---- OpenNest/Forms/CuttingInspectionPanel.cs | 6 +-- OpenNest/Forms/EditDrawingSetForm.Designer.cs | 4 +- OpenNest/Forms/ImportBomToSetForm.Designer.cs | 13 +++-- OpenNest/Forms/OptionsForm.cs | 2 +- OpenNest/Forms/ReleaseSetForm.Designer.cs | 4 +- 12 files changed, 106 insertions(+), 33 deletions(-) create mode 100644 OpenNest.WinForms.Tests/Controls/GridStyleTests.cs diff --git a/OpenNest.WinForms.Tests/Controls/GridStyleTests.cs b/OpenNest.WinForms.Tests/Controls/GridStyleTests.cs new file mode 100644 index 00000000..580b5081 --- /dev/null +++ b/OpenNest.WinForms.Tests/Controls/GridStyleTests.cs @@ -0,0 +1,48 @@ +using System.Windows.Forms; +using OpenNest.Controls; + +namespace OpenNest.WinForms.Tests.Controls; + +public class GridStyleTests +{ + [Fact] + public void ColumnsSizeToFirstContentThenKeepTheUsersWidths() => ArchUnitsTests.RunSta(() => + { + using var form = new Form { Width = 1200, Height = 400, ShowInTaskbar = false }; + var grid = GridStyle.Apply(new DataGridView { Dock = DockStyle.Fill, AllowUserToAddRows = false }); + var shortColumn = new DataGridViewTextBoxColumn { HeaderText = "Qty", Width = 300 }; + var longColumn = new DataGridViewTextBoxColumn { HeaderText = "Name", Width = 40 }; + var hugeColumn = new DataGridViewTextBoxColumn { HeaderText = "Notes", Width = 40 }; + grid.Columns.AddRange(shortColumn, longColumn, hugeColumn); + form.Controls.Add(grid); + form.Show(); + Pump(grid); + Assert.Equal(300, shortColumn.Width); // no rows yet: nothing is measured + + grid.Rows.Add("1", "A fairly long drawing name", new string('W', 200)); + Pump(grid); + + Assert.True(shortColumn.Width < 300); + Assert.True(longColumn.Width > 40); + Assert.Equal(400, hugeColumn.Width); + Assert.All(grid.Columns.Cast(), column => + { + Assert.Equal(DataGridViewAutoSizeColumnMode.None, column.InheritedAutoSizeMode); + Assert.True(column.Resizable == DataGridViewTriState.True || column.Resizable == DataGridViewTriState.NotSet); + }); + + longColumn.Width = 77; + grid.Rows.Add("2", new string('X', 80), ""); + Pump(grid); + Assert.Equal(77, longColumn.Width); + }); + + private static void Pump(Control control) + { + for (var i = 0; i < 5; i++) + { + control.Refresh(); + Application.DoEvents(); + } + } +} diff --git a/OpenNest/Controls/DrawingLibraryView.Designer.cs b/OpenNest/Controls/DrawingLibraryView.Designer.cs index edc7bcca..0a76554f 100644 --- a/OpenNest/Controls/DrawingLibraryView.Designer.cs +++ b/OpenNest/Controls/DrawingLibraryView.Designer.cs @@ -199,7 +199,7 @@ namespace OpenNest.Controls // // nestNameColumn // - nestNameColumn.AutoSizeMode = System.Windows.Forms.DataGridViewAutoSizeColumnMode.Fill; + nestNameColumn.Width = 240; nestNameColumn.HeaderText = "Name"; nestNameColumn.MinimumWidth = 160; nestNameColumn.Name = "nestNameColumn"; @@ -366,7 +366,7 @@ namespace OpenNest.Controls // // nameColumn // - nameColumn.AutoSizeMode = System.Windows.Forms.DataGridViewAutoSizeColumnMode.Fill; + nameColumn.Width = 240; nameColumn.HeaderText = "Name"; nameColumn.MinimumWidth = 160; nameColumn.Name = "nameColumn"; diff --git a/OpenNest/Controls/GridStyle.cs b/OpenNest/Controls/GridStyle.cs index c52f4e44..38f6c611 100644 --- a/OpenNest/Controls/GridStyle.cs +++ b/OpenNest/Controls/GridStyle.cs @@ -6,11 +6,13 @@ namespace OpenNest.Controls; /// /// The shared look of every in the desktop app, taken from the /// database nest browser: flat light headers, faint grid lines, a soft blue selection and -/// fixed row heights. Behaviour (editing, selection mode, column sizing) stays with each grid. +/// fixed row heights. Columns size once to their first content and are then the user's to +/// resize. Other behaviour (editing, selection mode) stays with each grid. /// public static class GridStyle { public const int RowHeight = 22; + private const int MaxInitialColumnWidth = 400; private static readonly Color HeaderBackColor = Color.FromArgb(245, 245, 245); private static readonly Color HeaderForeColor = Color.FromArgb(90, 90, 90); @@ -42,6 +44,35 @@ public static class GridStyle grid.RowHeadersDefaultCellStyle.SelectionBackColor = SelectionBackColor; grid.RowHeadersDefaultCellStyle.SelectionForeColor = Color.Black; grid.RowHeadersDefaultCellStyle.Alignment = DataGridViewContentAlignment.MiddleRight; + SizeColumnsOnceToContent(grid); return grid; } + + /// + /// Widens each column to fit its header and the rows first shown, once, then leaves the + /// widths to the user. Columns never stretch to fill the grid. + /// + private static void SizeColumnsOnceToContent(DataGridView grid) + { + // The first paint with rows is the first time they are on screen to be measured. The + // empty new-row placeholder does not count: a grid that starts empty keeps its widths + // until it has content. + void OnPaint(object sender, PaintEventArgs e) + { + var newRowShown = grid.NewRowIndex >= 0 && grid.Rows[grid.NewRowIndex].Displayed; + if (grid.DisplayedRowCount(false) - (newRowShown ? 1 : 0) <= 0) + return; + grid.Paint -= OnPaint; + grid.BeginInvoke(() => + { + if (grid.IsDisposed) + return; + grid.AutoResizeColumns(DataGridViewAutoSizeColumnsMode.DisplayedCells); + foreach (DataGridViewColumn column in grid.Columns) + column.Width = System.Math.Min(column.Width, MaxInitialColumnWidth); + }); + } + + grid.Paint += OnPaint; + } } diff --git a/OpenNest/Controls/PostSettingsEditor.cs b/OpenNest/Controls/PostSettingsEditor.cs index 0c220428..b3b062a5 100644 --- a/OpenNest/Controls/PostSettingsEditor.cs +++ b/OpenNest/Controls/PostSettingsEditor.cs @@ -258,14 +258,13 @@ namespace OpenNest.Controls Height = 200, AllowUserToAddRows = true, AllowUserToDeleteRows = true, - AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.Fill, RowHeadersWidth = 28, SelectionMode = DataGridViewSelectionMode.FullRowSelect, MultiSelect = true, Margin = new Padding(0, 4, 0, 0), }); - table.Columns.Add(new DataGridViewTextBoxColumn { HeaderText = field.KeyHeader, FillWeight = 65 }); - table.Columns.Add(new DataGridViewTextBoxColumn { HeaderText = field.ValueHeader, FillWeight = 35 }); + table.Columns.Add(new DataGridViewTextBoxColumn { HeaderText = field.KeyHeader, Width = 260 }); + table.Columns.Add(new DataGridViewTextBoxColumn { HeaderText = field.ValueHeader, Width = 140 }); if (map != null) { diff --git a/OpenNest/Controls/SetReleasesView.Designer.cs b/OpenNest/Controls/SetReleasesView.Designer.cs index ca62f8cc..0b249f98 100644 --- a/OpenNest/Controls/SetReleasesView.Designer.cs +++ b/OpenNest/Controls/SetReleasesView.Designer.cs @@ -176,7 +176,7 @@ namespace OpenNest.Controls releaseNotesColumn.HeaderText = "Notes"; releaseNotesColumn.Name = "releaseNotesColumn"; releaseNotesColumn.SortMode = System.Windows.Forms.DataGridViewColumnSortMode.NotSortable; - releaseNotesColumn.AutoSizeMode = System.Windows.Forms.DataGridViewAutoSizeColumnMode.Fill; + releaseNotesColumn.Width = 300; // // nestsGroup // diff --git a/OpenNest/Forms/AddReleaseDrawingsForm.Designer.cs b/OpenNest/Forms/AddReleaseDrawingsForm.Designer.cs index 1247027b..6eed9121 100644 --- a/OpenNest/Forms/AddReleaseDrawingsForm.Designer.cs +++ b/OpenNest/Forms/AddReleaseDrawingsForm.Designer.cs @@ -138,7 +138,7 @@ namespace OpenNest.Forms // // libraryCustomerColumn // - libraryCustomerColumn.AutoSizeMode = System.Windows.Forms.DataGridViewAutoSizeColumnMode.Fill; + libraryCustomerColumn.Width = 150; libraryCustomerColumn.HeaderText = "Customer"; libraryCustomerColumn.MinimumWidth = 60; libraryCustomerColumn.Name = "libraryCustomerColumn"; diff --git a/OpenNest/Forms/BomImportForm.Designer.cs b/OpenNest/Forms/BomImportForm.Designer.cs index eb7ddcc0..75976651 100644 --- a/OpenNest/Forms/BomImportForm.Designer.cs +++ b/OpenNest/Forms/BomImportForm.Designer.cs @@ -270,7 +270,6 @@ namespace OpenNest.Forms // dgvParts.AllowUserToAddRows = false; dgvParts.AllowUserToDeleteRows = false; - dgvParts.AutoSizeColumnsMode = System.Windows.Forms.DataGridViewAutoSizeColumnsMode.Fill; 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"; @@ -288,7 +287,7 @@ namespace OpenNest.Forms // colItemNum // colItemNum.DataPropertyName = "ItemNum"; - colItemNum.FillWeight = 40F; + colItemNum.Width = 60; colItemNum.HeaderText = "Item #"; colItemNum.Name = "colItemNum"; colItemNum.ReadOnly = true; @@ -297,7 +296,7 @@ namespace OpenNest.Forms // colFileName // colFileName.DataPropertyName = "FileName"; - colFileName.FillWeight = 120F; + colFileName.Width = 180; colFileName.HeaderText = "File Name"; colFileName.Name = "colFileName"; colFileName.ReadOnly = true; @@ -306,7 +305,7 @@ namespace OpenNest.Forms // colDescription // colDescription.DataPropertyName = "Description"; - colDescription.FillWeight = 150F; + colDescription.Width = 220; colDescription.HeaderText = "Description"; colDescription.Name = "colDescription"; colDescription.ReadOnly = true; @@ -315,7 +314,7 @@ namespace OpenNest.Forms // colMaterial // colMaterial.DataPropertyName = "Material"; - colMaterial.FillWeight = 110F; + colMaterial.Width = 140; colMaterial.HeaderText = "Material"; colMaterial.Name = "colMaterial"; colMaterial.SortMode = System.Windows.Forms.DataGridViewColumnSortMode.NotSortable; @@ -327,7 +326,7 @@ namespace OpenNest.Forms thicknessCellStyle.Format = "0.####"; colThickness.DataPropertyName = "Thickness"; colThickness.DefaultCellStyle = thicknessCellStyle; - colThickness.FillWeight = 70F; + colThickness.Width = 80; colThickness.HeaderText = "Thickness"; colThickness.Name = "colThickness"; colThickness.SortMode = System.Windows.Forms.DataGridViewColumnSortMode.NotSortable; @@ -338,7 +337,7 @@ namespace OpenNest.Forms qtyCellStyle.DataSourceNullValue = null; colQty.DataPropertyName = "Qty"; colQty.DefaultCellStyle = qtyCellStyle; - colQty.FillWeight = 50F; + colQty.Width = 60; colQty.HeaderText = "Qty"; colQty.Name = "colQty"; colQty.SortMode = System.Windows.Forms.DataGridViewColumnSortMode.NotSortable; @@ -346,7 +345,7 @@ namespace OpenNest.Forms // colStatus // colStatus.DataPropertyName = "StatusText"; - colStatus.FillWeight = 100F; + colStatus.Width = 140; colStatus.HeaderText = "Status"; colStatus.Name = "colStatus"; colStatus.ReadOnly = true; @@ -363,7 +362,6 @@ namespace OpenNest.Forms // dgvGroups.AllowUserToAddRows = false; dgvGroups.AllowUserToDeleteRows = false; - dgvGroups.AutoSizeColumnsMode = System.Windows.Forms.DataGridViewAutoSizeColumnsMode.Fill; dgvGroups.Dock = System.Windows.Forms.DockStyle.Fill; dgvGroups.Name = "dgvGroups"; dgvGroups.RowHeadersVisible = false; diff --git a/OpenNest/Forms/CuttingInspectionPanel.cs b/OpenNest/Forms/CuttingInspectionPanel.cs index 5ee0f705..fb4ff9cf 100644 --- a/OpenNest/Forms/CuttingInspectionPanel.cs +++ b/OpenNest/Forms/CuttingInspectionPanel.cs @@ -212,17 +212,15 @@ internal sealed class CuttingInspectionPanel : UserControl warningGrid.Columns.Add(new DataGridViewTextBoxColumn { HeaderText = name, - Width = 48, + Width = name is "Issue" or "Status" ? 260 : 48, SortMode = DataGridViewColumnSortMode.NotSortable, - AutoSizeMode = name == "Issue" ? DataGridViewAutoSizeColumnMode.Fill : DataGridViewAutoSizeColumnMode.None }); foreach (var name in new[] { "Plate", "Part", "Status" }) partsGrid.Columns.Add(new DataGridViewTextBoxColumn { HeaderText = name, - Width = 48, + Width = name is "Issue" or "Status" ? 260 : 48, SortMode = DataGridViewColumnSortMode.NotSortable, - AutoSizeMode = name == "Status" ? DataGridViewAutoSizeColumnMode.Fill : DataGridViewAutoSizeColumnMode.None }); Padding = new Padding(10); planButton.Click += (_, _) => StartCurrentMode(); diff --git a/OpenNest/Forms/EditDrawingSetForm.Designer.cs b/OpenNest/Forms/EditDrawingSetForm.Designer.cs index 82b2f8ee..09ebc899 100644 --- a/OpenNest/Forms/EditDrawingSetForm.Designer.cs +++ b/OpenNest/Forms/EditDrawingSetForm.Designer.cs @@ -286,7 +286,7 @@ namespace OpenNest.Forms // // libraryCustomerColumn // - libraryCustomerColumn.AutoSizeMode = System.Windows.Forms.DataGridViewAutoSizeColumnMode.Fill; + libraryCustomerColumn.Width = 120; libraryCustomerColumn.HeaderText = "Customer"; libraryCustomerColumn.MinimumWidth = 60; libraryCustomerColumn.Name = "libraryCustomerColumn"; @@ -476,7 +476,7 @@ namespace OpenNest.Forms // // warningColumn // - warningColumn.AutoSizeMode = System.Windows.Forms.DataGridViewAutoSizeColumnMode.Fill; + warningColumn.Width = 200; warningColumn.HeaderText = "Note"; warningColumn.MinimumWidth = 60; warningColumn.Name = "warningColumn"; diff --git a/OpenNest/Forms/ImportBomToSetForm.Designer.cs b/OpenNest/Forms/ImportBomToSetForm.Designer.cs index b86ba0a7..4b7370e0 100644 --- a/OpenNest/Forms/ImportBomToSetForm.Designer.cs +++ b/OpenNest/Forms/ImportBomToSetForm.Designer.cs @@ -190,7 +190,6 @@ namespace OpenNest.Forms drawingGrid.AllowUserToAddRows = false; drawingGrid.AllowUserToDeleteRows = false; drawingGrid.AllowUserToResizeRows = false; - drawingGrid.AutoSizeColumnsMode = System.Windows.Forms.DataGridViewAutoSizeColumnsMode.Fill; drawingGrid.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize; drawingGrid.Columns.AddRange(new System.Windows.Forms.DataGridViewColumn[] { drawingColumn, materialColumn, thicknessColumn, quantityColumn, libraryColumn, noteColumn }); drawingGrid.Dock = System.Windows.Forms.DockStyle.Fill; @@ -203,21 +202,21 @@ namespace OpenNest.Forms // // drawingColumn // - drawingColumn.FillWeight = 130F; + drawingColumn.Width = 180; drawingColumn.HeaderText = "Drawing"; drawingColumn.Name = "drawingColumn"; drawingColumn.SortMode = System.Windows.Forms.DataGridViewColumnSortMode.NotSortable; // // materialColumn // - materialColumn.FillWeight = 80F; + materialColumn.Width = 120; materialColumn.HeaderText = "Material"; materialColumn.Name = "materialColumn"; materialColumn.SortMode = System.Windows.Forms.DataGridViewColumnSortMode.NotSortable; // // thicknessColumn // - thicknessColumn.FillWeight = 50F; + thicknessColumn.Width = 80; thicknessColumn.HeaderText = "Thickness"; thicknessColumn.Name = "thicknessColumn"; thicknessColumn.SortMode = System.Windows.Forms.DataGridViewColumnSortMode.NotSortable; @@ -225,7 +224,7 @@ namespace OpenNest.Forms // quantityColumn // quantityColumn.DefaultCellStyle = new System.Windows.Forms.DataGridViewCellStyle { Alignment = System.Windows.Forms.DataGridViewContentAlignment.MiddleRight }; - quantityColumn.FillWeight = 40F; + quantityColumn.Width = 60; quantityColumn.HeaderText = "Qty"; quantityColumn.Name = "quantityColumn"; quantityColumn.SortMode = System.Windows.Forms.DataGridViewColumnSortMode.NotSortable; @@ -233,14 +232,14 @@ namespace OpenNest.Forms // // libraryColumn // - libraryColumn.FillWeight = 120F; + libraryColumn.Width = 180; libraryColumn.HeaderText = "Drawing library"; libraryColumn.Name = "libraryColumn"; libraryColumn.SortMode = System.Windows.Forms.DataGridViewColumnSortMode.NotSortable; // // noteColumn // - noteColumn.FillWeight = 120F; + noteColumn.Width = 200; noteColumn.HeaderText = "Note"; noteColumn.Name = "noteColumn"; noteColumn.SortMode = System.Windows.Forms.DataGridViewColumnSortMode.NotSortable; diff --git a/OpenNest/Forms/OptionsForm.cs b/OpenNest/Forms/OptionsForm.cs index e790cbf8..291fb12d 100644 --- a/OpenNest/Forms/OptionsForm.cs +++ b/OpenNest/Forms/OptionsForm.cs @@ -46,7 +46,7 @@ namespace OpenNest.Forms Name = "Name", HeaderText = "Strategy", ReadOnly = true, - AutoSizeMode = DataGridViewAutoSizeColumnMode.Fill, + Width = 220, } ); diff --git a/OpenNest/Forms/ReleaseSetForm.Designer.cs b/OpenNest/Forms/ReleaseSetForm.Designer.cs index 81e11031..7c61ec6e 100644 --- a/OpenNest/Forms/ReleaseSetForm.Designer.cs +++ b/OpenNest/Forms/ReleaseSetForm.Designer.cs @@ -209,7 +209,7 @@ namespace OpenNest.Forms // // setNameColumn // - setNameColumn.AutoSizeMode = System.Windows.Forms.DataGridViewAutoSizeColumnMode.Fill; + setNameColumn.Width = 260; setNameColumn.HeaderText = "Set or drawing"; setNameColumn.Name = "setNameColumn"; setNameColumn.ReadOnly = true; @@ -434,7 +434,7 @@ namespace OpenNest.Forms // // statusColumn // - statusColumn.AutoSizeMode = System.Windows.Forms.DataGridViewAutoSizeColumnMode.Fill; + statusColumn.Width = 200; statusColumn.HeaderText = "Status"; statusColumn.Name = "statusColumn"; statusColumn.ReadOnly = true;