From d27bdba3fce4c43e4adb6b4a44ff63428d2a95ca Mon Sep 17 00:00:00 2001 From: Erik Darling <2136037+erikdarlingdata@users.noreply.github.com> Date: Sat, 2 May 2026 10:25:54 -0400 Subject: [PATCH] =?UTF-8?q?Fix=20#916=20=E2=80=94=20Memory=20tab=20tooltip?= =?UTF-8?q?=20stops=20working=20after=20returning=20to=20tab?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit WPF Popup with PlacementTarget = chart can wedge when the user navigates away from a TabItem mid-hover: TabControl unloads the parent without firing MouseLeave on the chart, so _popup.IsOpen stays true with a stale anchor. On return, OnMouseMove sets IsOpen = true but it is already true — the assignment is a no-op and the popup never re-anchors or appears. Memory tab is the most visible victim because it has 6 charts inside a nested TabControl, multiplying the chance of a wedged popup, but the bug is general to ChartHoverHelper. - Force _popup.IsOpen = false on chart Loaded / Unloaded / IsVisibleChanged. - In OnMouseMove, toggle IsOpen off then on so WPF re-evaluates placement even when the popup believes it is already open. Co-Authored-By: Claude Opus 4.7 (1M context) --- Dashboard/Helpers/ChartHoverHelper.cs | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/Dashboard/Helpers/ChartHoverHelper.cs b/Dashboard/Helpers/ChartHoverHelper.cs index 2ff0ab1..ef36220 100644 --- a/Dashboard/Helpers/ChartHoverHelper.cs +++ b/Dashboard/Helpers/ChartHoverHelper.cs @@ -53,6 +53,16 @@ public ChartHoverHelper(ScottPlot.WPF.WpfPlot chart, string unit) chart.MouseMove += OnMouseMove; chart.MouseLeave += OnMouseLeave; + + /* Tab switching can leave the popup wedged: WPF unloads the parent TabItem + without firing MouseLeave, so IsOpen stays true with a stale anchor. + When the chart becomes visible again, OnMouseMove sets IsOpen = true + but it is already true, so the popup never re-anchors and never shows. + Force-close on every visibility/load transition so the next mouse move + re-opens cleanly. */ + chart.IsVisibleChanged += OnChartVisibilityChanged; + chart.Unloaded += OnChartUnloaded; + chart.Loaded += OnChartLoaded; } public string Unit { get => _unit; set => _unit = value; } @@ -61,11 +71,23 @@ public void Dispose() { _chart.MouseMove -= OnMouseMove; _chart.MouseLeave -= OnMouseLeave; + _chart.IsVisibleChanged -= OnChartVisibilityChanged; + _chart.Unloaded -= OnChartUnloaded; + _chart.Loaded -= OnChartLoaded; _popup.IsOpen = false; _scatters.Clear(); _barPlots.Clear(); } + private void OnChartVisibilityChanged(object sender, DependencyPropertyChangedEventArgs e) => + _popup.IsOpen = false; + + private void OnChartUnloaded(object sender, RoutedEventArgs e) => + _popup.IsOpen = false; + + private void OnChartLoaded(object sender, RoutedEventArgs e) => + _popup.IsOpen = false; + public void Clear() { _scatters.Clear(); @@ -207,6 +229,11 @@ pick the series closest in Y (nearest line to cursor). */ _text.Text = $"{bestLabel}\n{valueFormatted} {_unit}\n{time:HH:mm:ss}"; _popup.HorizontalOffset = pos.X + 15; _popup.VerticalOffset = pos.Y + 15; + /* Toggle if already open so WPF re-evaluates the placement target. + Without this, a popup that was IsOpen = true when its TabItem was + unloaded stays "open" with a stale anchor and never appears on + return — the assignment below is a no-op. */ + if (_popup.IsOpen) _popup.IsOpen = false; _popup.IsOpen = true; } else