fix: marshal timer callbacks to UI thread to prevent GDI+ threading exception

System.Timers.Timer fires on thread pool threads, causing GraphicsPath
objects to be accessed concurrently by hover detection and OnPaint,
triggering "Object is currently in use elsewhere" in DrawParts.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-09 10:41:18 -04:00
parent 6a30828fad
commit 640814fdf6

View File

@@ -621,30 +621,30 @@ namespace OpenNest.Controls
private void redrawTimer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
Invalidate();
if (IsDisposed || !IsHandleCreated) return;
BeginInvoke(new System.Action(Invalidate));
}
private void hoverTimer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
if (IsDisposed || !IsHandleCreated) return;
BeginInvoke(new System.Action(HoverCheck));
}
private void HoverCheck()
{
var graphPt = PointControlToGraph(hoverPoint);
LayoutPart hitPart = null;
try
for (var i = parts.Count - 1; i >= 0; --i)
{
for (var i = parts.Count - 1; i >= 0; --i)
if (parts[i].Path.GetBounds().Contains(graphPt) &&
parts[i].Path.IsVisible(graphPt))
{
if (parts[i].Path.GetBounds().Contains(graphPt) &&
parts[i].Path.IsVisible(graphPt))
{
hitPart = parts[i];
break;
}
hitPart = parts[i];
break;
}
}
catch (InvalidOperationException)
{
// GraphicsPath in use by paint thread — skip this hover tick
return;
}
hoveredPart = hitPart;
showTooltip = hitPart != null;