mirror of
https://github.com/ajisaacs/OpenNest.git
synced 2026-11-04 18:11:35 -05:00
fix(ui): stop a grid's keyboard tooltip before the grid is disposed
Browsing nests in Database mode crashed with ObjectDisposedException on DataGridView: selecting another nest drops the embedded version history, and WinForms leaves its grid's keyboard-tooltip auto-pop timer running. When the timer fired it hid the tooltip through the disposed grid. GridStyle now hides that tooltip when a styled grid's window is destroyed, which stops the timer for every grid in the app. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
588ae5ed09
commit
6f4e872c03
2 files changed
+49
-1
No files matched your search
@@ -1,3 +1,4 @@
|
||||
using System.Reflection;
|
||||
using System.Windows.Forms;
|
||||
using OpenNest.Controls;
|
||||
|
||||
@@ -79,6 +80,33 @@ public class GridStyleTests
|
||||
Assert.Equal(column.Width, firstPaintWidth());
|
||||
});
|
||||
|
||||
[Fact]
|
||||
public void DisposingAGridStopsItsShowingKeyboardToolTip() => ArchUnitsTests.RunSta(() =>
|
||||
{
|
||||
// WinForms leaves the keyboard tooltip's auto-pop timer running after the grid is
|
||||
// disposed; when it fires it hides the tooltip through the disposed grid and throws.
|
||||
const BindingFlags members = BindingFlags.Instance | BindingFlags.NonPublic;
|
||||
using var form = new Form { Width = 600, Height = 300, ShowInTaskbar = false };
|
||||
var grid = GridStyle.Apply(new DataGridView { Dock = DockStyle.Fill, AllowUserToAddRows = false });
|
||||
grid.Columns.Add("Version", "Version");
|
||||
grid.Rows.Add("2");
|
||||
form.Controls.Add(grid);
|
||||
form.Show();
|
||||
Pump(grid);
|
||||
|
||||
// As when the keyboard moves to a cell in the embedded version history.
|
||||
var toolTip = (ToolTip)typeof(DataGridView).GetProperty("KeyboardToolTip", members).GetValue(grid);
|
||||
typeof(ToolTip).GetMethod("ShowKeyboardToolTip", members)
|
||||
.Invoke(toolTip, new object[] { "2", grid.Rows[0].Cells[0], 3000 });
|
||||
var timer = typeof(ToolTip).GetField("_timer", members);
|
||||
Assert.NotNull(timer.GetValue(toolTip));
|
||||
|
||||
form.Controls.Remove(grid);
|
||||
grid.Dispose();
|
||||
|
||||
Assert.Null(timer.GetValue(toolTip));
|
||||
});
|
||||
|
||||
private static Func<int> WidthAtFirstPaint(DataGridView grid, DataGridViewColumn column)
|
||||
{
|
||||
var width = -1;
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Drawing;
|
||||
using System.Reflection;
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace OpenNest.Controls;
|
||||
@@ -49,9 +50,28 @@ public static class GridStyle
|
||||
grid.RowHeadersDefaultCellStyle.Alignment = DataGridViewContentAlignment.MiddleRight;
|
||||
if (sizeColumns)
|
||||
SizeColumnsOnceToContent(grid);
|
||||
grid.HandleDestroyed += HideToolTipsOnDestroy;
|
||||
return grid;
|
||||
}
|
||||
|
||||
private static readonly PropertyInfo KeyboardToolTipProperty = typeof(DataGridView).GetProperty(
|
||||
"KeyboardToolTip", BindingFlags.Instance | BindingFlags.NonPublic);
|
||||
|
||||
/// <summary>
|
||||
/// Hides the grid's keyboard tooltip while the grid's window still exists. WinForms does not
|
||||
/// stop that tooltip's auto-pop timer when the grid is disposed, and the timer later hides the
|
||||
/// tooltip through the disposed grid, which throws (as when the database browser drops its
|
||||
/// embedded version history). Hiding it stops the timer. The tooltip is internal to WinForms,
|
||||
/// so a runtime without it is left as it is.
|
||||
/// </summary>
|
||||
private static void HideToolTipsOnDestroy(object sender, EventArgs e)
|
||||
{
|
||||
var grid = (DataGridView)sender;
|
||||
if (grid.RecreatingHandle || KeyboardToolTipProperty?.GetValue(grid) is not ToolTip toolTip)
|
||||
return;
|
||||
toolTip.Hide(grid);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Widens each column to fit its header and its first rows, once, then leaves the widths
|
||||
/// to the user. Columns never stretch to fill the grid.
|
||||
|
||||
Reference in new issue
Block a user