From a0802658ea0a4328ce35c8c006d7d80ccbd48da5 Mon Sep 17 00:00:00 2001
From: BornToBeRoot <16019165+BornToBeRoot@users.noreply.github.com>
Date: Sun, 16 Aug 2026 17:13:18 +0200
Subject: [PATCH 1/5] Feature: Add start & pause button per group in ping
monitor
---
.../Resources/Strings.Designer.cs | 20 +-
.../Resources/Strings.resx | 6 +
.../ViewModels/PingMonitorHostViewModel.cs | 22 ++
.../Views/PingMonitorHostView.xaml | 227 +++++++++++++-----
Website/docs/changelog/next-release.md | 1 +
5 files changed, 211 insertions(+), 65 deletions(-)
diff --git a/Source/NETworkManager.Localization/Resources/Strings.Designer.cs b/Source/NETworkManager.Localization/Resources/Strings.Designer.cs
index 3cf04e06e4..2966877d93 100644
--- a/Source/NETworkManager.Localization/Resources/Strings.Designer.cs
+++ b/Source/NETworkManager.Localization/Resources/Strings.Designer.cs
@@ -1898,7 +1898,25 @@ public static string CloseGroup {
return ResourceManager.GetString("CloseGroup", resourceCulture);
}
}
-
+
+ ///
+ /// Looks up a localized string similar to Start group.
+ ///
+ public static string StartGroup {
+ get {
+ return ResourceManager.GetString("StartGroup", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Pause group.
+ ///
+ public static string PauseGroup {
+ get {
+ return ResourceManager.GetString("PauseGroup", resourceCulture);
+ }
+ }
+
///
/// Looks up a localized string similar to Closing in {0} seconds....
///
diff --git a/Source/NETworkManager.Localization/Resources/Strings.resx b/Source/NETworkManager.Localization/Resources/Strings.resx
index 9c69a5b26d..fb08530760 100644
--- a/Source/NETworkManager.Localization/Resources/Strings.resx
+++ b/Source/NETworkManager.Localization/Resources/Strings.resx
@@ -3892,6 +3892,12 @@ Try again in a few seconds.
Close group
+
+ Start group
+
+
+ Pause group
+
Run command... (Ctrl+Shift+P)
diff --git a/Source/NETworkManager/ViewModels/PingMonitorHostViewModel.cs b/Source/NETworkManager/ViewModels/PingMonitorHostViewModel.cs
index f50cf2cafe..a25bf063a8 100644
--- a/Source/NETworkManager/ViewModels/PingMonitorHostViewModel.cs
+++ b/Source/NETworkManager/ViewModels/PingMonitorHostViewModel.cs
@@ -239,6 +239,28 @@ private void CloseGroupAction(object group)
RemoveGroup(group.ToString());
}
+ ///
+ /// Gets the command to start every host in a group that is not currently running.
+ ///
+ public ICommand StartGroupCommand => new RelayCommand(StartGroupAction);
+
+ private void StartGroupAction(object group)
+ {
+ foreach (var host in Hosts.Where(host => host.Group.Equals(group.ToString()) && !host.IsRunning))
+ host.Start();
+ }
+
+ ///
+ /// Gets the command to pause every host in a group that is currently running.
+ ///
+ public ICommand PauseGroupCommand => new RelayCommand(PauseGroupAction);
+
+ private void PauseGroupAction(object group)
+ {
+ foreach (var host in Hosts.Where(host => host.Group.Equals(group.ToString()) && host.IsRunning))
+ host.Stop();
+ }
+
#endregion
#region Methods
diff --git a/Source/NETworkManager/Views/PingMonitorHostView.xaml b/Source/NETworkManager/Views/PingMonitorHostView.xaml
index 46ddaa631e..2394d72076 100644
--- a/Source/NETworkManager/Views/PingMonitorHostView.xaml
+++ b/Source/NETworkManager/Views/PingMonitorHostView.xaml
@@ -196,8 +196,8 @@
+ Visibility="{Binding Path=Hosts.Count, Converter={StaticResource ResourceKey=IntNotZeroToVisibilityCollapsedConverter}}"
+ Background="Transparent">
@@ -216,92 +216,191 @@
+
-
+ Orientation="Horizontal"
+ VerticalAlignment="Center">
+
-
+
-
-
+
+
-
+ Style="{StaticResource ResourceKey=DefaultTextBlock}"
+ Foreground="{DynamicResource ResourceKey=MahApps.Brushes.Gray5}"
+ Margin="6,0,6,0" />
+
-
+
-
-
+
+
+ Style="{StaticResource ResourceKey=DefaultTextBlock}"
+ Foreground="{DynamicResource ResourceKey=MahApps.Brushes.Gray5}"
+ Margin="6,0,6,0"
+ Visibility="{Binding ElementName=PausedText, Path=Visibility}" />
+ Style="{StaticResource ResourceKey=DefaultTextBlock}"
+ Foreground="{DynamicResource ResourceKey=MahApps.Brushes.Gray5}">
-
+
-
-
+
+
-
+
-
+
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -337,8 +436,8 @@
Focusable="False"
Style="{StaticResource ResourceKey=VerticalGridSplitter}" />
+ ProfileItemTemplate="{StaticResource ResourceKey=PingMonitorProfileItemTemplate}"
+ ProfileContextMenuExtraItems="{StaticResource ResourceKey=PingMonitorProfileContextMenuExtraItems}"
+ RunProfileCommand="{Binding Path=PingProfileCommand}" />
\ No newline at end of file
diff --git a/Website/docs/changelog/next-release.md b/Website/docs/changelog/next-release.md
index beeb7c68ab..c143c31b3d 100644
--- a/Website/docs/changelog/next-release.md
+++ b/Website/docs/changelog/next-release.md
@@ -55,6 +55,7 @@ Release date: **xx.xx.2026**
**Ping Monitor**
- Added a live count of hosts up/down (and paused, if any) per group, next to the group's close button. [#3572](https://github.com/BornToBeRoot/NETworkManager/pull/3572)
+- Added **Start** and **Pause** buttons to each group header (shown next to the close button on mouse-over), to start every paused host in the group or pause every running one at once. [#XXXX](https://github.com/BornToBeRoot/NETworkManager/pull/XXXX)
- Reworked each monitored host's card: the collapsed quick info now shows labeled values (`Received: X · Lost: Y · Packet loss: Z%`) instead of unlabeled numbers, the expanded view shows only the latency chart (with more room, since Hostname/IP address are already visible in the header) and the last status change time moved to a tooltip on the connectivity icon. The **Status change** field was also renamed to **Last status change**. [#3572](https://github.com/BornToBeRoot/NETworkManager/pull/3572)
- Host input now accepts newline-separated hosts (one per line, e.g. a column pasted from Excel), converted automatically to the semicolon-separated form. [#3568](https://github.com/BornToBeRoot/NETworkManager/pull/3568)
- Host input now supports shorthand IPv4 ranges like `192.168.0.1-100`, in addition to the existing `192.168.0.0-192.168.0.100` and `192.168.[0-100].1` range formats. [#3568](https://github.com/BornToBeRoot/NETworkManager/pull/3568)
From a626bd02b1859ac724b5096ef517e2bd44c5a8cb Mon Sep 17 00:00:00 2001
From: BornToBeRoot <16019165+BornToBeRoot@users.noreply.github.com>
Date: Sun, 16 Aug 2026 17:16:33 +0200
Subject: [PATCH 2/5] Docs: update docs #3575
---
Website/docs/application/ping-monitor.md | 12 ++++++++++++
Website/docs/changelog/next-release.md | 2 +-
2 files changed, 13 insertions(+), 1 deletion(-)
diff --git a/Website/docs/application/ping-monitor.md b/Website/docs/application/ping-monitor.md
index 6391f00de4..d731b89879 100644
--- a/Website/docs/application/ping-monitor.md
+++ b/Website/docs/application/ping-monitor.md
@@ -52,6 +52,18 @@ You can interact with the chart to inspect past results:
When you zoom or pan, the chart leaves live mode and stops scrolling. A **Live** button then appears in the top-right corner of the chart — click it to return to live mode and resume auto-scrolling.
+### Groups
+
+Hosts are grouped by their profile group (or **Hosts** if none is set). Each group header shows a live count of hosts that are up, down and paused (**Paused** is only shown while at least one host in the group is paused).
+
+Hover over a group header to reveal its action buttons:
+
+| Action | Description |
+|--------|-------------|
+| **Start** | Starts every host in the group that isn't currently running |
+| **Pause** | Pauses every host in the group that's currently running |
+| **Close** | Stops and removes every host in the group |
+
### Context menu
Right-click a monitored host (anywhere except the chart) to open the context menu:
diff --git a/Website/docs/changelog/next-release.md b/Website/docs/changelog/next-release.md
index c143c31b3d..919e0a8504 100644
--- a/Website/docs/changelog/next-release.md
+++ b/Website/docs/changelog/next-release.md
@@ -55,7 +55,7 @@ Release date: **xx.xx.2026**
**Ping Monitor**
- Added a live count of hosts up/down (and paused, if any) per group, next to the group's close button. [#3572](https://github.com/BornToBeRoot/NETworkManager/pull/3572)
-- Added **Start** and **Pause** buttons to each group header (shown next to the close button on mouse-over), to start every paused host in the group or pause every running one at once. [#XXXX](https://github.com/BornToBeRoot/NETworkManager/pull/XXXX)
+- Added **Start** and **Pause** buttons to each group header (shown next to the close button on mouse-over), to start every paused host in the group or pause every running one at once. [#3575](https://github.com/BornToBeRoot/NETworkManager/pull/3575)
- Reworked each monitored host's card: the collapsed quick info now shows labeled values (`Received: X · Lost: Y · Packet loss: Z%`) instead of unlabeled numbers, the expanded view shows only the latency chart (with more room, since Hostname/IP address are already visible in the header) and the last status change time moved to a tooltip on the connectivity icon. The **Status change** field was also renamed to **Last status change**. [#3572](https://github.com/BornToBeRoot/NETworkManager/pull/3572)
- Host input now accepts newline-separated hosts (one per line, e.g. a column pasted from Excel), converted automatically to the semicolon-separated form. [#3568](https://github.com/BornToBeRoot/NETworkManager/pull/3568)
- Host input now supports shorthand IPv4 ranges like `192.168.0.1-100`, in addition to the existing `192.168.0.0-192.168.0.100` and `192.168.[0-100].1` range formats. [#3568](https://github.com/BornToBeRoot/NETworkManager/pull/3568)
From 6dce9177155d24c3bfcc1f27fb3d0073f77b254a Mon Sep 17 00:00:00 2001
From: BornToBeRoot <16019165+BornToBeRoot@users.noreply.github.com>
Date: Sun, 16 Aug 2026 17:56:33 +0200
Subject: [PATCH 3/5] Fix: Touch / keyboard behaviour - auto close after 20
seconds
---
.../Resources/Strings.Designer.cs | 9 ++
.../Resources/Strings.resx | 3 +
.../Views/PingMonitorHostView.xaml | 50 +++++++----
.../Views/PingMonitorHostView.xaml.cs | 86 +++++++++++++++++++
Website/docs/application/ping-monitor.md | 2 +-
Website/docs/changelog/next-release.md | 2 +-
6 files changed, 133 insertions(+), 19 deletions(-)
diff --git a/Source/NETworkManager.Localization/Resources/Strings.Designer.cs b/Source/NETworkManager.Localization/Resources/Strings.Designer.cs
index 2966877d93..8df05e8a07 100644
--- a/Source/NETworkManager.Localization/Resources/Strings.Designer.cs
+++ b/Source/NETworkManager.Localization/Resources/Strings.Designer.cs
@@ -1908,6 +1908,15 @@ public static string StartGroup {
}
}
+ ///
+ /// Looks up a localized string similar to Group actions.
+ ///
+ public static string GroupActions {
+ get {
+ return ResourceManager.GetString("GroupActions", resourceCulture);
+ }
+ }
+
///
/// Looks up a localized string similar to Pause group.
///
diff --git a/Source/NETworkManager.Localization/Resources/Strings.resx b/Source/NETworkManager.Localization/Resources/Strings.resx
index fb08530760..5533818793 100644
--- a/Source/NETworkManager.Localization/Resources/Strings.resx
+++ b/Source/NETworkManager.Localization/Resources/Strings.resx
@@ -3895,6 +3895,9 @@ Try again in a few seconds.
Start group
+
+ Group actions
+
Pause group
diff --git a/Source/NETworkManager/Views/PingMonitorHostView.xaml b/Source/NETworkManager/Views/PingMonitorHostView.xaml
index 2394d72076..e56b3f785b 100644
--- a/Source/NETworkManager/Views/PingMonitorHostView.xaml
+++ b/Source/NETworkManager/Views/PingMonitorHostView.xaml
@@ -208,7 +208,8 @@
-
+
@@ -292,23 +293,38 @@
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+ Visibility="{Binding Path=IsChecked, ElementName=GroupActionsToggle, Converter={StaticResource ResourceKey=BooleanToVisibilityCollapsedConverter}}">
diff --git a/Source/NETworkManager/Views/PingMonitorHostView.xaml.cs b/Source/NETworkManager/Views/PingMonitorHostView.xaml.cs
index 14ef172187..c9617551dc 100644
--- a/Source/NETworkManager/Views/PingMonitorHostView.xaml.cs
+++ b/Source/NETworkManager/Views/PingMonitorHostView.xaml.cs
@@ -1,9 +1,21 @@
+using System;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Controls.Primitives;
+using System.Windows.Input;
+using System.Windows.Threading;
using NETworkManager.ViewModels;
namespace NETworkManager.Views;
public partial class PingMonitorHostView
{
+ ///
+ /// Delay before a group's action buttons (Start/Pause/Close), revealed via mouse-over or by
+ /// clicking/tapping the "..." toggle, are hidden again after the last interaction.
+ ///
+ private static readonly TimeSpan GroupActionsAutoCloseDelay = TimeSpan.FromSeconds(20);
+
private readonly PingMonitorHostViewModel _viewModel = new();
public PingMonitorHostView()
@@ -27,4 +39,78 @@ public void OnViewVisible()
{
_viewModel.OnViewVisible();
}
+
+ ///
+ /// Reveals the group's action buttons and cancels any pending auto-close, so hovering the
+ /// header keeps them visible for as long as the mouse stays over it.
+ ///
+ private void GroupHeader_MouseEnter(object sender, MouseEventArgs e)
+ {
+ if (((Grid)sender).FindName("GroupActionsToggle") is not ToggleButton toggle)
+ return;
+
+ StopGroupActionsAutoCloseTimer(toggle);
+ toggle.IsChecked = true;
+ }
+
+ ///
+ /// Starts the auto-close countdown once the mouse leaves the header, instead of hiding the
+ /// action buttons immediately - this avoids them flickering away while moving the mouse
+ /// from the header onto one of the buttons.
+ ///
+ private void GroupHeader_MouseLeave(object sender, MouseEventArgs e)
+ {
+ if (((Grid)sender).FindName("GroupActionsToggle") is not ToggleButton toggle)
+ return;
+
+ if (toggle.IsChecked == true)
+ StartGroupActionsAutoCloseTimer(toggle);
+ }
+
+ ///
+ /// Starts the auto-close countdown when the toggle is checked by a click/tap rather than by
+ /// hovering the header ( already handles the hover case,
+ /// and cancels this if the mouse is still over the header).
+ ///
+ private void GroupActionsToggle_Checked(object sender, RoutedEventArgs e)
+ {
+ var toggle = (ToggleButton)sender;
+
+ if (!IsMouseOverGroupHeader(toggle))
+ StartGroupActionsAutoCloseTimer(toggle);
+ }
+
+ private void GroupActionsToggle_Unchecked(object sender, RoutedEventArgs e)
+ {
+ StopGroupActionsAutoCloseTimer((ToggleButton)sender);
+ }
+
+ private static bool IsMouseOverGroupHeader(ToggleButton toggle)
+ {
+ return toggle.Parent is Grid headerGrid && headerGrid.IsMouseOver;
+ }
+
+ private static void StartGroupActionsAutoCloseTimer(ToggleButton toggle)
+ {
+ StopGroupActionsAutoCloseTimer(toggle);
+
+ var timer = new DispatcherTimer { Interval = GroupActionsAutoCloseDelay };
+ timer.Tick += (_, _) =>
+ {
+ StopGroupActionsAutoCloseTimer(toggle);
+ toggle.IsChecked = false;
+ };
+
+ toggle.Tag = timer;
+ timer.Start();
+ }
+
+ private static void StopGroupActionsAutoCloseTimer(ToggleButton toggle)
+ {
+ if (toggle.Tag is not DispatcherTimer timer)
+ return;
+
+ timer.Stop();
+ toggle.Tag = null;
+ }
}
diff --git a/Website/docs/application/ping-monitor.md b/Website/docs/application/ping-monitor.md
index d731b89879..0c5d7425f2 100644
--- a/Website/docs/application/ping-monitor.md
+++ b/Website/docs/application/ping-monitor.md
@@ -56,7 +56,7 @@ When you zoom or pan, the chart leaves live mode and stops scrolling. A **Live**
Hosts are grouped by their profile group (or **Hosts** if none is set). Each group header shows a live count of hosts that are up, down and paused (**Paused** is only shown while at least one host in the group is paused).
-Hover over a group header to reveal its action buttons:
+Hover over a group header to reveal its action buttons. Without a mouse (touch or keyboard), click/tap the **⋮** icon on the right instead - the buttons then stay visible for 20 seconds, or as long as you keep hovering.
| Action | Description |
|--------|-------------|
diff --git a/Website/docs/changelog/next-release.md b/Website/docs/changelog/next-release.md
index 919e0a8504..41200f1d7b 100644
--- a/Website/docs/changelog/next-release.md
+++ b/Website/docs/changelog/next-release.md
@@ -55,7 +55,7 @@ Release date: **xx.xx.2026**
**Ping Monitor**
- Added a live count of hosts up/down (and paused, if any) per group, next to the group's close button. [#3572](https://github.com/BornToBeRoot/NETworkManager/pull/3572)
-- Added **Start** and **Pause** buttons to each group header (shown next to the close button on mouse-over), to start every paused host in the group or pause every running one at once. [#3575](https://github.com/BornToBeRoot/NETworkManager/pull/3575)
+- Added **Start** and **Pause** buttons to each group header, to start every paused host in the group or pause every running one at once. They're shown next to the close button on mouse-over, or by clicking/tapping the **⋮** icon (for touch and keyboard use). [#3575](https://github.com/BornToBeRoot/NETworkManager/pull/3575)
- Reworked each monitored host's card: the collapsed quick info now shows labeled values (`Received: X · Lost: Y · Packet loss: Z%`) instead of unlabeled numbers, the expanded view shows only the latency chart (with more room, since Hostname/IP address are already visible in the header) and the last status change time moved to a tooltip on the connectivity icon. The **Status change** field was also renamed to **Last status change**. [#3572](https://github.com/BornToBeRoot/NETworkManager/pull/3572)
- Host input now accepts newline-separated hosts (one per line, e.g. a column pasted from Excel), converted automatically to the semicolon-separated form. [#3568](https://github.com/BornToBeRoot/NETworkManager/pull/3568)
- Host input now supports shorthand IPv4 ranges like `192.168.0.1-100`, in addition to the existing `192.168.0.0-192.168.0.100` and `192.168.[0-100].1` range formats. [#3568](https://github.com/BornToBeRoot/NETworkManager/pull/3568)
From 1aad253b4ed9949337c2c78647e6ebeb739d11a3 Mon Sep 17 00:00:00 2001
From: BornToBeRoot <16019165+BornToBeRoot@users.noreply.github.com>
Date: Sun, 16 Aug 2026 18:48:43 +0200
Subject: [PATCH 4/5] Fix: Claude review
---
Source/NETworkManager/Views/PingMonitorHostView.xaml | 4 +++-
.../NETworkManager/Views/PingMonitorHostView.xaml.cs | 12 +++++++++++-
2 files changed, 14 insertions(+), 2 deletions(-)
diff --git a/Source/NETworkManager/Views/PingMonitorHostView.xaml b/Source/NETworkManager/Views/PingMonitorHostView.xaml
index e56b3f785b..8faf93a788 100644
--- a/Source/NETworkManager/Views/PingMonitorHostView.xaml
+++ b/Source/NETworkManager/Views/PingMonitorHostView.xaml
@@ -208,7 +208,8 @@
-
@@ -300,6 +301,7 @@
ToolTip="{x:Static Member=localization:Strings.GroupActions}"
Checked="GroupActionsToggle_Checked"
Unchecked="GroupActionsToggle_Unchecked"
+ Unloaded="GroupActionsToggle_Unloaded"
Visibility="{Binding Path=IsChecked, RelativeSource={RelativeSource Mode=Self}, Converter={StaticResource ResourceKey=BooleanReverseToVisibilityCollapsedConverter}}">
diff --git a/Source/NETworkManager/Views/PingMonitorHostView.xaml.cs b/Source/NETworkManager/Views/PingMonitorHostView.xaml.cs
index c9617551dc..d25c6ccc94 100644
--- a/Source/NETworkManager/Views/PingMonitorHostView.xaml.cs
+++ b/Source/NETworkManager/Views/PingMonitorHostView.xaml.cs
@@ -85,9 +85,19 @@ private void GroupActionsToggle_Unchecked(object sender, RoutedEventArgs e)
StopGroupActionsAutoCloseTimer((ToggleButton)sender);
}
+ ///
+ /// Stops any pending auto-close timer when the group (and its toggle) is torn down, e.g.
+ /// because the group was closed - otherwise the timer would keep the detached toggle alive
+ /// until it fires.
+ ///
+ private void GroupActionsToggle_Unloaded(object sender, RoutedEventArgs e)
+ {
+ StopGroupActionsAutoCloseTimer((ToggleButton)sender);
+ }
+
private static bool IsMouseOverGroupHeader(ToggleButton toggle)
{
- return toggle.Parent is Grid headerGrid && headerGrid.IsMouseOver;
+ return toggle.FindName("GroupHeaderGrid") is Grid headerGrid && headerGrid.IsMouseOver;
}
private static void StartGroupActionsAutoCloseTimer(ToggleButton toggle)
From 2c08bfaf317ff9e24065c88f46d655ccbd007f27 Mon Sep 17 00:00:00 2001
From: BornToBeRoot <16019165+BornToBeRoot@users.noreply.github.com>
Date: Sun, 16 Aug 2026 21:12:49 +0200
Subject: [PATCH 5/5] Fix: Use context menu instead of buttons
---
.../Views/PingMonitorHostView.xaml | 166 ++++++------------
.../Views/PingMonitorHostView.xaml.cs | 97 +---------
Website/docs/application/ping-monitor.md | 2 +-
Website/docs/changelog/next-release.md | 4 +-
4 files changed, 69 insertions(+), 200 deletions(-)
diff --git a/Source/NETworkManager/Views/PingMonitorHostView.xaml b/Source/NETworkManager/Views/PingMonitorHostView.xaml
index 8faf93a788..048c2fe6c1 100644
--- a/Source/NETworkManager/Views/PingMonitorHostView.xaml
+++ b/Source/NETworkManager/Views/PingMonitorHostView.xaml
@@ -26,6 +26,10 @@
+
+
@@ -208,9 +212,7 @@
-
+
@@ -294,15 +296,13 @@
-
+
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
diff --git a/Source/NETworkManager/Views/PingMonitorHostView.xaml.cs b/Source/NETworkManager/Views/PingMonitorHostView.xaml.cs
index d25c6ccc94..42366d6a88 100644
--- a/Source/NETworkManager/Views/PingMonitorHostView.xaml.cs
+++ b/Source/NETworkManager/Views/PingMonitorHostView.xaml.cs
@@ -1,21 +1,11 @@
-using System;
using System.Windows;
using System.Windows.Controls;
-using System.Windows.Controls.Primitives;
-using System.Windows.Input;
-using System.Windows.Threading;
using NETworkManager.ViewModels;
namespace NETworkManager.Views;
public partial class PingMonitorHostView
{
- ///
- /// Delay before a group's action buttons (Start/Pause/Close), revealed via mouse-over or by
- /// clicking/tapping the "..." toggle, are hidden again after the last interaction.
- ///
- private static readonly TimeSpan GroupActionsAutoCloseDelay = TimeSpan.FromSeconds(20);
-
private readonly PingMonitorHostViewModel _viewModel = new();
public PingMonitorHostView()
@@ -41,86 +31,15 @@ public void OnViewVisible()
}
///
- /// Reveals the group's action buttons and cancels any pending auto-close, so hovering the
- /// header keeps them visible for as long as the mouse stays over it.
- ///
- private void GroupHeader_MouseEnter(object sender, MouseEventArgs e)
- {
- if (((Grid)sender).FindName("GroupActionsToggle") is not ToggleButton toggle)
- return;
-
- StopGroupActionsAutoCloseTimer(toggle);
- toggle.IsChecked = true;
- }
-
- ///
- /// Starts the auto-close countdown once the mouse leaves the header, instead of hiding the
- /// action buttons immediately - this avoids them flickering away while moving the mouse
- /// from the header onto one of the buttons.
- ///
- private void GroupHeader_MouseLeave(object sender, MouseEventArgs e)
- {
- if (((Grid)sender).FindName("GroupActionsToggle") is not ToggleButton toggle)
- return;
-
- if (toggle.IsChecked == true)
- StartGroupActionsAutoCloseTimer(toggle);
- }
-
- ///
- /// Starts the auto-close countdown when the toggle is checked by a click/tap rather than by
- /// hovering the header ( already handles the hover case,
- /// and cancels this if the mouse is still over the header).
- ///
- private void GroupActionsToggle_Checked(object sender, RoutedEventArgs e)
- {
- var toggle = (ToggleButton)sender;
-
- if (!IsMouseOverGroupHeader(toggle))
- StartGroupActionsAutoCloseTimer(toggle);
- }
-
- private void GroupActionsToggle_Unchecked(object sender, RoutedEventArgs e)
- {
- StopGroupActionsAutoCloseTimer((ToggleButton)sender);
- }
-
- ///
- /// Stops any pending auto-close timer when the group (and its toggle) is torn down, e.g.
- /// because the group was closed - otherwise the timer would keep the detached toggle alive
- /// until it fires.
+ /// Opens the group actions context menu on a normal click (not just right-click), so it
+ /// works the same way for mouse, touch and keyboard activation. PlacementTarget is set
+ /// explicitly since it isn't populated automatically when opening the menu this way, and
+ /// the menu items rely on it to reach the group name (see Button.Tag in the XAML).
///
- private void GroupActionsToggle_Unloaded(object sender, RoutedEventArgs e)
- {
- StopGroupActionsAutoCloseTimer((ToggleButton)sender);
- }
-
- private static bool IsMouseOverGroupHeader(ToggleButton toggle)
+ private void GroupActionsButton_Click(object sender, RoutedEventArgs e)
{
- return toggle.FindName("GroupHeaderGrid") is Grid headerGrid && headerGrid.IsMouseOver;
- }
-
- private static void StartGroupActionsAutoCloseTimer(ToggleButton toggle)
- {
- StopGroupActionsAutoCloseTimer(toggle);
-
- var timer = new DispatcherTimer { Interval = GroupActionsAutoCloseDelay };
- timer.Tick += (_, _) =>
- {
- StopGroupActionsAutoCloseTimer(toggle);
- toggle.IsChecked = false;
- };
-
- toggle.Tag = timer;
- timer.Start();
- }
-
- private static void StopGroupActionsAutoCloseTimer(ToggleButton toggle)
- {
- if (toggle.Tag is not DispatcherTimer timer)
- return;
-
- timer.Stop();
- toggle.Tag = null;
+ var button = (Button)sender;
+ button.ContextMenu.PlacementTarget = button;
+ button.ContextMenu.IsOpen = true;
}
}
diff --git a/Website/docs/application/ping-monitor.md b/Website/docs/application/ping-monitor.md
index 0c5d7425f2..03e953395f 100644
--- a/Website/docs/application/ping-monitor.md
+++ b/Website/docs/application/ping-monitor.md
@@ -56,7 +56,7 @@ When you zoom or pan, the chart leaves live mode and stops scrolling. A **Live**
Hosts are grouped by their profile group (or **Hosts** if none is set). Each group header shows a live count of hosts that are up, down and paused (**Paused** is only shown while at least one host in the group is paused).
-Hover over a group header to reveal its action buttons. Without a mouse (touch or keyboard), click/tap the **⋮** icon on the right instead - the buttons then stay visible for 20 seconds, or as long as you keep hovering.
+Click/tap the **⋮** icon on the right of a group header to open its action menu:
| Action | Description |
|--------|-------------|
diff --git a/Website/docs/changelog/next-release.md b/Website/docs/changelog/next-release.md
index 41200f1d7b..33e29c9986 100644
--- a/Website/docs/changelog/next-release.md
+++ b/Website/docs/changelog/next-release.md
@@ -54,8 +54,8 @@ Release date: **xx.xx.2026**
**Ping Monitor**
-- Added a live count of hosts up/down (and paused, if any) per group, next to the group's close button. [#3572](https://github.com/BornToBeRoot/NETworkManager/pull/3572)
-- Added **Start** and **Pause** buttons to each group header, to start every paused host in the group or pause every running one at once. They're shown next to the close button on mouse-over, or by clicking/tapping the **⋮** icon (for touch and keyboard use). [#3575](https://github.com/BornToBeRoot/NETworkManager/pull/3575)
+- Added a live count of hosts up/down (and paused, if any) to each group header. [#3572](https://github.com/BornToBeRoot/NETworkManager/pull/3572)
+- Added **Start** and **Pause** actions to each group header (next to the existing **Close**), to start every paused host in the group or pause every running one at once. Click/tap the **⋮** icon to open the group's action menu. [#3575](https://github.com/BornToBeRoot/NETworkManager/pull/3575)
- Reworked each monitored host's card: the collapsed quick info now shows labeled values (`Received: X · Lost: Y · Packet loss: Z%`) instead of unlabeled numbers, the expanded view shows only the latency chart (with more room, since Hostname/IP address are already visible in the header) and the last status change time moved to a tooltip on the connectivity icon. The **Status change** field was also renamed to **Last status change**. [#3572](https://github.com/BornToBeRoot/NETworkManager/pull/3572)
- Host input now accepts newline-separated hosts (one per line, e.g. a column pasted from Excel), converted automatically to the semicolon-separated form. [#3568](https://github.com/BornToBeRoot/NETworkManager/pull/3568)
- Host input now supports shorthand IPv4 ranges like `192.168.0.1-100`, in addition to the existing `192.168.0.0-192.168.0.100` and `192.168.[0-100].1` range formats. [#3568](https://github.com/BornToBeRoot/NETworkManager/pull/3568)