diff --git a/.claude/skills/feature-review/references/conventions.md b/.claude/skills/feature-review/references/conventions.md index 0715193403..fdfb50a29a 100644 --- a/.claude/skills/feature-review/references/conventions.md +++ b/.claude/skills/feature-review/references/conventions.md @@ -84,7 +84,19 @@ Reference: `HostsFileEditorView.xaml`, `FirewallView.xaml`. `SelectedResults` is changed in place (no change notification): selection-dependent properties are raised in `OnContextMenuOpened()` called from the view's `ContextMenu_Opened`. - Two context menus: `RowContextMenu` (one row) and `MultiRowContextMenu` (several rows), switched by a - `DataTrigger` on `SelectedItems.Count`. `Opened="ContextMenu_Opened"` sets the menu's `DataContext`. + `DataTrigger` on `SelectedItems.Count`. +- A context menu is not part of the visual tree and doesn't inherit the `DataContext`: the menu gets + `DataContext="{Binding Data, Source={StaticResource BindingProxy}}"` in XAML, with + `` in the view's own resources, declared before + the menus. The bindings inside the menu are plain bindings (`{Binding EditEntryCommand}`). Don't set the + `DataContext` in `ContextMenu_Opened` (until the menu is opened, its bindings fail and are logged as XAML binding + errors). `Opened` is only for extra work, e.g. `OnContextMenuOpened()` or the custom commands of the IP Scanner. +- No bindings that have no element to resolve against: no `VisualBrush` / `BitmapImage` with a binding in a style + setter or as a freezable resource. Bind the property of the element with a converter instead + (`VisualToVisualBrushConverter`, `ApplicationNameToIconBrushConverter`, `Image Source="{Binding Uri}"`). A static + `VisualBrush` (`Visual="{iconPacks:Material Kind=...}"`) is fine. `MenuItem`s created outside a menu (e.g. extra + items in an `x:Array`) are fine: the implicit `DefaultMenuItem` style sets the content alignment directly instead + of the MahApps binding to the parent. New `MenuItem` styles are `BasedOn="{StaticResource DefaultMenuItem}"`. - Order: Enable / Disable (only the applicable one) → Edit... → Copy as... → Delete → separator → Copy (submenu, `CopyMenuItem` style) → Export... - Icons (16×16 `Rectangle`, `MahApps.Brushes.Gray3`, `VisualBrush`): Enable `Material ToggleSwitchOutline`, diff --git a/Source/NETworkManager.Converters/ProfileToPortScannerPortsConverter.cs b/Source/NETworkManager.Converters/ProfileToPortScannerPortsConverter.cs new file mode 100644 index 0000000000..b89fde8830 --- /dev/null +++ b/Source/NETworkManager.Converters/ProfileToPortScannerPortsConverter.cs @@ -0,0 +1,42 @@ +using System; +using System.Globalization; +using System.Windows.Data; +using NETworkManager.Profiles; + +namespace NETworkManager.Converters; + +/// +/// Convert a to the ports the Port Scanner scans for it (own ports, otherwise the ports +/// of its group, port profiles with their current name). +/// +public sealed class ProfileToPortScannerPortsConverter : IValueConverter +{ + /// + /// Convert a to the ports to scan (see + /// ). + /// + /// Object from type . + /// + /// + /// + /// Ports to scan, empty if neither the profile nor its group has ports. + public object Convert(object value, Type targetType, object parameter, CultureInfo culture) + { + return value is not ProfileInfo profile + ? string.Empty + : Profiles.Application.PortScanner.GetPorts(profile, out _); + } + + /// + /// !!! Method not implemented !!! + /// + /// + /// + /// + /// + /// + public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) + { + throw new NotImplementedException(); + } +} diff --git a/Source/NETworkManager.Models/Network/IPScanner.cs b/Source/NETworkManager.Models/Network/IPScanner.cs index f62f11742b..88053c2601 100644 --- a/Source/NETworkManager.Models/Network/IPScanner.cs +++ b/Source/NETworkManager.Models/Network/IPScanner.cs @@ -28,6 +28,9 @@ public sealed class IPScanner(IPScannerOptions options) /// private static readonly TimeSpan MaxHostStartInterval = TimeSpan.FromMilliseconds(10); + // Data sent with the ping, created once (see IPScannerOptions.ICMPBufferSize) + private readonly byte[] _icmpBuffer = new byte[options.ICMPBufferSize]; + private int _progressValue; private readonly InterlockedCounter _hostsUp = new(); private readonly InterlockedCounter _hostsDown = new(); @@ -211,7 +214,7 @@ private async Task ScanHostAsync(IPAddress ipAddress, ParallelOptions portScanPa var portAnswered = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); var pingTask = PingProbe.PingAsync(ipAddress, options.ICMPTimeout, options.ICMPAttempts, - options.ICMPBuffer, cancellationToken); + _icmpBuffer, cancellationToken); var portScanTask = options.PortScanEnabled ? PortScanAsync(ipAddress, portScanParallelOptions, () => portAnswered.TrySetResult(), cancellationToken) diff --git a/Source/NETworkManager.Models/Network/IPScannerOptions.cs b/Source/NETworkManager.Models/Network/IPScannerOptions.cs index 54e08dba1a..39047590e9 100644 --- a/Source/NETworkManager.Models/Network/IPScannerOptions.cs +++ b/Source/NETworkManager.Models/Network/IPScannerOptions.cs @@ -27,9 +27,10 @@ public record IPScannerOptions public required int ICMPTimeout { get; init; } /// - /// Size of the buffer used in the ping in bytes. + /// Size of the buffer used in the ping in bytes. A size, not the buffer, so options with the same values are + /// equal (records compare arrays by reference). /// - public required byte[] ICMPBuffer { get; init; } + public required int ICMPBufferSize { get; init; } /// /// Resolve the hostname for an IP address. diff --git a/Source/NETworkManager.Models/Network/Ping.cs b/Source/NETworkManager.Models/Network/Ping.cs index fc06a056d2..fc64412f19 100644 --- a/Source/NETworkManager.Models/Network/Ping.cs +++ b/Source/NETworkManager.Models/Network/Ping.cs @@ -1,7 +1,6 @@ using System; using System.Net; using System.Net.NetworkInformation; -using System.Net.Sockets; using System.Threading; using System.Threading.Tasks; using NETworkManager.Utilities; @@ -26,9 +25,9 @@ public sealed class Ping public int Timeout = 4000; /// - /// The buffer to send with the ping request. Default is 32 bytes. + /// The size in bytes of the buffer to send with the ping request. Default is 32 bytes. /// - public byte[] Buffer = new byte[32]; + public int BufferSize = 32; /// /// The Time to Live (TTL) value for the ping request. Default is 64. @@ -123,6 +122,8 @@ public void SendAsync(IPAddress ipAddress, CancellationToken cancellationToken) var errorCount = 0; + var buffer = new byte[BufferSize]; + var options = new PingOptions { Ttl = TTL, @@ -139,7 +140,7 @@ public void SendAsync(IPAddress ipAddress, CancellationToken cancellationToken) var timestamp = DateTime.Now; // Send ping - var pingReply = ping.Send(ipAddress, Timeout, Buffer, options); + var pingReply = ping.Send(ipAddress, Timeout, buffer, options); // Reset the error count (if no exception was thrown) errorCount = 0; @@ -152,15 +153,8 @@ public void SendAsync(IPAddress ipAddress, CancellationToken cancellationToken) } else { - if (ipAddress.AddressFamily == AddressFamily.InterNetwork) - OnPingReceived(new PingReceivedArgs( - new PingInfo(timestamp, pingReply.Address, hostname, - pingReply.Buffer.Length, pingReply.RoundtripTime, pingReply.Options!.Ttl, - pingReply.Status))); - else - OnPingReceived(new PingReceivedArgs( - new PingInfo(timestamp, pingReply.Address, hostname, - pingReply.Buffer.Length, pingReply.RoundtripTime, pingReply.Status))); + OnPingReceived(new PingReceivedArgs( + PingInfo.FromSuccessfulReply(timestamp, pingReply, hostname))); } } catch (PingException ex) diff --git a/Source/NETworkManager.Models/Network/PingInfo.cs b/Source/NETworkManager.Models/Network/PingInfo.cs index 4a15bf88cb..e3c5c5bcfc 100644 --- a/Source/NETworkManager.Models/Network/PingInfo.cs +++ b/Source/NETworkManager.Models/Network/PingInfo.cs @@ -122,6 +122,22 @@ public PingInfo(DateTime timestamp, IPAddress ipAddress, string hostname, int by Status = status; } + /// + /// Creates a from a successful ping reply. The TTL is only available for IPv4. + /// + /// Timestamp when the ping was sent. + /// Successful reply of the ping. + /// Hostname of the host. + /// Information of the ping. + internal static PingInfo FromSuccessfulReply(DateTime timestamp, PingReply pingReply, string hostname = null) + { + return pingReply.Address.AddressFamily == AddressFamily.InterNetwork + ? new PingInfo(timestamp, pingReply.Address, hostname, pingReply.Buffer.Length, pingReply.RoundtripTime, + pingReply.Options!.Ttl, pingReply.Status) + : new PingInfo(timestamp, pingReply.Address, hostname, pingReply.Buffer.Length, pingReply.RoundtripTime, + pingReply.Status); + } + /// /// Timestamp when the ping was sent. /// diff --git a/Source/NETworkManager.Models/Network/PingProbe.cs b/Source/NETworkManager.Models/Network/PingProbe.cs index b5dff7a4cc..cc109803bf 100644 --- a/Source/NETworkManager.Models/Network/PingProbe.cs +++ b/Source/NETworkManager.Models/Network/PingProbe.cs @@ -1,7 +1,6 @@ using System; using System.Net; using System.Net.NetworkInformation; -using System.Net.Sockets; using System.Threading; using System.Threading.Tasks; @@ -43,26 +42,7 @@ public static async Task PingAsync(IPAddress ipAddress, int timeoutMs, // Success if (pingReply is { Status: IPStatus.Success }) - { - switch (ipAddress.AddressFamily) - { - case AddressFamily.InterNetwork: - return new PingInfo( - timestamp, - pingReply.Address, - pingReply.Buffer.Length, - pingReply.RoundtripTime, - pingReply.Options!.Ttl, - pingReply.Status); - case AddressFamily.InterNetworkV6: - return new PingInfo( - timestamp, - pingReply.Address, - pingReply.Buffer.Length, - pingReply.RoundtripTime, - pingReply.Status); - } - } + return PingInfo.FromSuccessfulReply(timestamp, pingReply); // Failed pingInfo = new PingInfo(timestamp, ipAddress, pingReply.Status); diff --git a/Source/NETworkManager.Models/Network/PortScanner.cs b/Source/NETworkManager.Models/Network/PortScanner.cs index 72be0ce9a0..370ac31b5e 100644 --- a/Source/NETworkManager.Models/Network/PortScanner.cs +++ b/Source/NETworkManager.Models/Network/PortScanner.cs @@ -19,6 +19,7 @@ public sealed class PortScanner public PortScanner(PortScannerOptions options) { _options = options; + _icmpBuffer = new byte[options.ICMPBufferSize]; } #endregion @@ -69,6 +70,9 @@ public PortScanner(PortScannerOptions options) private readonly PortScannerOptions _options; + // Data sent with the ping before the scan, created once (see PortScannerOptions.ICMPBufferSize) + private readonly byte[] _icmpBuffer; + /// /// Gets the number of ports scanned so far. Thread-safe; may be read from any thread while the scan is /// running. @@ -394,7 +398,7 @@ await Parallel.ForEachAsync(hosts, pingParallelOptions, async (host, ct) => private async Task IsReachableAsync(IPAddress ipAddress, int portCount, CancellationToken cancellationToken) { var pingInfo = await PingProbe.PingAsync(ipAddress, _options.ICMPTimeout, _options.ICMPAttempts, - _options.ICMPBuffer, cancellationToken).ConfigureAwait(false); + _icmpBuffer, cancellationToken).ConfigureAwait(false); if (pingInfo.Status == IPStatus.Success) return true; diff --git a/Source/NETworkManager.Models/Network/PortScannerOptions.cs b/Source/NETworkManager.Models/Network/PortScannerOptions.cs index 961e868fdb..ce447114d9 100644 --- a/Source/NETworkManager.Models/Network/PortScannerOptions.cs +++ b/Source/NETworkManager.Models/Network/PortScannerOptions.cs @@ -33,7 +33,7 @@ public record PortScannerOptions /// /// Ping every host before its ports are scanned (with , and - /// ), a host that doesn't answer is not scanned. + /// ), a host that doesn't answer is not scanned. /// public required bool PingHostsBeforeScan { get; init; } @@ -49,9 +49,10 @@ public record PortScannerOptions public required int ICMPTimeout { get; init; } /// - /// Data sent with the ping before the scan (see ). + /// Size in bytes of the data sent with the ping before the scan (see ). A size, + /// not the buffer, so options with the same values are equal (records compare arrays by reference). /// - public required byte[] ICMPBuffer { get; init; } + public required int ICMPBufferSize { get; init; } /// /// Resolve the hostname for an IP address. diff --git a/Source/NETworkManager.Models/Network/Traceroute.cs b/Source/NETworkManager.Models/Network/Traceroute.cs index 0115c98963..d0f6337622 100644 --- a/Source/NETworkManager.Models/Network/Traceroute.cs +++ b/Source/NETworkManager.Models/Network/Traceroute.cs @@ -17,6 +17,9 @@ public sealed class Traceroute private readonly TracerouteOptions _options; + // Data sent with the ping, created once (see TracerouteOptions.BufferSize) + private readonly byte[] _buffer; + #endregion #region Constructor @@ -24,6 +27,7 @@ public sealed class Traceroute public Traceroute(TracerouteOptions options) { _options = options; + _buffer = new byte[options.BufferSize]; } #endregion @@ -55,7 +59,7 @@ public void TraceAsync(IPAddress ipAddress, CancellationToken cancellationToken) { stopwatch.Start(); - pingReply = ping.Send(ipAddress, _options.Timeout, _options.Buffer, + pingReply = ping.Send(ipAddress, _options.Timeout, _buffer, new PingOptions { Ttl = i1, DontFragment = _options.DontFragment }); stopwatch.Stop(); diff --git a/Source/NETworkManager.Models/Network/TracerouteOptions.cs b/Source/NETworkManager.Models/Network/TracerouteOptions.cs index 2233fb378f..1cbaf5e941 100644 --- a/Source/NETworkManager.Models/Network/TracerouteOptions.cs +++ b/Source/NETworkManager.Models/Network/TracerouteOptions.cs @@ -9,16 +9,16 @@ public class TracerouteOptions /// Create an instance of with parameters. /// /// Timeout in milliseconds after which a ping is considered lost. - /// Size of the buffer used in the ping in bytes. + /// Size of the buffer used in the ping in bytes. /// Maximum number of hops between the local and remote computer. /// Do not fragment the ping packet. /// Resolve the hostname for an IP address. /// Check IP geolocation via ip-api.com. - public TracerouteOptions(int timeout, byte[] buffer, int maximumHops, bool dontFragment, bool resolveHostname, + public TracerouteOptions(int timeout, int bufferSize, int maximumHops, bool dontFragment, bool resolveHostname, bool checkIPApiIPGeolocation) { Timeout = timeout; - Buffer = buffer; + BufferSize = bufferSize; MaximumHops = maximumHops; DontFragment = dontFragment; ResolveHostname = resolveHostname; @@ -33,7 +33,7 @@ public TracerouteOptions(int timeout, byte[] buffer, int maximumHops, bool dontF /// /// Size of the buffer used in the ping in bytes. /// - public byte[] Buffer { get; } + public int BufferSize { get; } /// /// Maximum number of hops between the local and remote computer. diff --git a/Source/NETworkManager.Settings/GlobalStaticConfiguration.cs b/Source/NETworkManager.Settings/GlobalStaticConfiguration.cs index d3ea08f0bf..48ad2b3032 100644 --- a/Source/NETworkManager.Settings/GlobalStaticConfiguration.cs +++ b/Source/NETworkManager.Settings/GlobalStaticConfiguration.cs @@ -46,6 +46,14 @@ public static class GlobalStaticConfiguration // ICMP config: maximum buffer of a ping (.NET's Ping throws for more than 65500 bytes) public static int ICMPBufferMaximum => 65500; + /// + /// Limits the buffer size of a ping from the settings to the limits of the input (1 to + /// ). A value edited in the settings file would throw or make every ping fail. + /// + /// Buffer size in bytes from the settings. + /// Buffer size in bytes within the limits. + public static int LimitICMPBuffer(int size) => Math.Clamp(size, 1, ICMPBufferMaximum); + // Profile config public static bool Profile_TagsMatchAny => true; public static bool Profile_ExpandProfileView => true; diff --git a/Source/NETworkManager/ViewModels/IPScannerViewModel.cs b/Source/NETworkManager/ViewModels/IPScannerViewModel.cs index c390d379af..4b92b976ab 100644 --- a/Source/NETworkManager/ViewModels/IPScannerViewModel.cs +++ b/Source/NETworkManager/ViewModels/IPScannerViewModel.cs @@ -901,9 +901,7 @@ private static IPScannerOptions CreateScanOptions(int[] ports) MaxPortThreads = SettingsManager.Current.IPScanner_MaxPortThreads, ICMPAttempts = SettingsManager.Current.IPScanner_ICMPAttempts, ICMPTimeout = SettingsManager.Current.IPScanner_ICMPTimeout, - // Limits of the input (a value edited in the settings file would throw or make every ping fail) - ICMPBuffer = new byte[Math.Clamp(SettingsManager.Current.IPScanner_ICMPBuffer, 1, - GlobalStaticConfiguration.ICMPBufferMaximum)], + ICMPBufferSize = GlobalStaticConfiguration.LimitICMPBuffer(SettingsManager.Current.IPScanner_ICMPBuffer), ResolveHostname = SettingsManager.Current.IPScanner_ResolveHostname, PortScanEnabled = SettingsManager.Current.IPScanner_PortScanEnabled, PortScanPorts = ports, diff --git a/Source/NETworkManager/ViewModels/PingMonitorViewModel.cs b/Source/NETworkManager/ViewModels/PingMonitorViewModel.cs index 1a91956a2b..15104c8bcc 100644 --- a/Source/NETworkManager/ViewModels/PingMonitorViewModel.cs +++ b/Source/NETworkManager/ViewModels/PingMonitorViewModel.cs @@ -571,9 +571,7 @@ public void Start() var ping = new Ping { Timeout = SettingsManager.Current.PingMonitor_Timeout, - // Limits of the input (a value edited in the settings file would throw or make every ping fail) - Buffer = new byte[Math.Clamp(SettingsManager.Current.PingMonitor_Buffer, 1, - GlobalStaticConfiguration.ICMPBufferMaximum)], + BufferSize = GlobalStaticConfiguration.LimitICMPBuffer(SettingsManager.Current.PingMonitor_Buffer), TTL = SettingsManager.Current.PingMonitor_TTL, DontFragment = SettingsManager.Current.PingMonitor_DontFragment, WaitTime = SettingsManager.Current.PingMonitor_WaitTime diff --git a/Source/NETworkManager/ViewModels/PortScannerViewModel.cs b/Source/NETworkManager/ViewModels/PortScannerViewModel.cs index 37c0164617..1c2811f163 100644 --- a/Source/NETworkManager/ViewModels/PortScannerViewModel.cs +++ b/Source/NETworkManager/ViewModels/PortScannerViewModel.cs @@ -1286,9 +1286,7 @@ private static PortScannerOptions CreateScanOptions() PingHostsBeforeScan = SettingsManager.Current.PortScanner_PingHostsBeforeScan, ICMPAttempts = SettingsManager.Current.PortScanner_ICMPAttempts, ICMPTimeout = SettingsManager.Current.PortScanner_ICMPTimeout, - // Limits of the input (a value edited in the settings file would throw or make every ping fail) - ICMPBuffer = new byte[Math.Clamp(SettingsManager.Current.PortScanner_ICMPBuffer, 1, - GlobalStaticConfiguration.ICMPBufferMaximum)], + ICMPBufferSize = GlobalStaticConfiguration.LimitICMPBuffer(SettingsManager.Current.PortScanner_ICMPBuffer), ResolveHostname = SettingsManager.Current.PortScanner_ResolveHostname, ShowAllResults = SettingsManager.Current.PortScanner_ShowAllResults }; diff --git a/Source/NETworkManager/ViewModels/ProfileViewModel.cs b/Source/NETworkManager/ViewModels/ProfileViewModel.cs index b810aef2ee..f6993ae211 100644 --- a/Source/NETworkManager/ViewModels/ProfileViewModel.cs +++ b/Source/NETworkManager/ViewModels/ProfileViewModel.cs @@ -14,6 +14,7 @@ using System.Collections.Generic; using System.ComponentModel; using System.Linq; +using System.Net; using System.Security; using System.Threading.Tasks; using System.Windows.Data; @@ -1010,12 +1011,21 @@ private set /// /// Checks if the query type of the profile fits the host (own or inherited), see - /// . + /// . Every address the lookup can parse is valid (also shortened IPv4 + /// forms like "127.1"), like in the DNS Lookup. /// private void ValidateDNSLookupQueryType() { - DNSLookup_QueryTypeIsValid = !DNSLookup_OverrideQueryType || DNSLookup_QueryType != QueryType.PTR || - IPAddressHelper.AreIPAddresses(DNSLookup_InheritHost ? Host : DNSLookup_Host); + if (!DNSLookup_OverrideQueryType || DNSLookup_QueryType != QueryType.PTR) + { + DNSLookup_QueryTypeIsValid = true; + return; + } + + var hosts = ((DNSLookup_InheritHost ? Host : DNSLookup_Host) ?? string.Empty).Split(';', + StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries); + + DNSLookup_QueryTypeIsValid = hosts.Length > 0 && hosts.All(x => IPAddress.TryParse(x, out _)); } #endregion diff --git a/Source/NETworkManager/ViewModels/TracerouteViewModel.cs b/Source/NETworkManager/ViewModels/TracerouteViewModel.cs index 748b20836a..d125f1caba 100644 --- a/Source/NETworkManager/ViewModels/TracerouteViewModel.cs +++ b/Source/NETworkManager/ViewModels/TracerouteViewModel.cs @@ -420,9 +420,7 @@ await DNSClientHelper.ResolveAorAaaaAsync(Host, { var traceroute = new Traceroute(new TracerouteOptions( SettingsManager.Current.Traceroute_Timeout, - // Limits of the input (a value edited in the settings file would throw or make every ping fail) - new byte[Math.Clamp(SettingsManager.Current.Traceroute_Buffer, 1, - GlobalStaticConfiguration.ICMPBufferMaximum)], + GlobalStaticConfiguration.LimitICMPBuffer(SettingsManager.Current.Traceroute_Buffer), SettingsManager.Current.Traceroute_MaximumHops, true, SettingsManager.Current.Traceroute_ResolveHostname, diff --git a/Source/NETworkManager/ViewModels/WebConsoleSettingsViewModel.cs b/Source/NETworkManager/ViewModels/WebConsoleSettingsViewModel.cs index bd52d3f4f2..45753fe5e2 100644 --- a/Source/NETworkManager/ViewModels/WebConsoleSettingsViewModel.cs +++ b/Source/NETworkManager/ViewModels/WebConsoleSettingsViewModel.cs @@ -149,6 +149,8 @@ private async Task DeleteBrowsingData() IsDeleteBrowsingDataStatusMessageDisplayed = false; + CoreWebView2Controller webView2Controller = null; + try { // Create a temporary WebView2 instance to clear browsing data @@ -157,12 +159,10 @@ private async Task DeleteBrowsingData() var windowHwnd = new WindowInteropHelper(Application.Current.MainWindow).Handle; - var webView2Controller = await webView2Environment.CreateCoreWebView2ControllerAsync(windowHwnd); + webView2Controller = await webView2Environment.CreateCoreWebView2ControllerAsync(windowHwnd); await webView2Controller.CoreWebView2.Profile.ClearBrowsingDataAsync(); - webView2Controller.Close(); - DeleteBrowsingDataStatusMessage = Strings.BrowsingDataSuccessfullyDeletedMessage; } catch (Exception ex) @@ -172,6 +172,11 @@ private async Task DeleteBrowsingData() DeleteBrowsingDataStatusMessage = Strings.BrowsingDataErrorMessage; } + finally + { + // Also on an error, otherwise the cache folder stays locked until the application is closed + webView2Controller?.Close(); + } IsDeleteBrowsingDataStatusMessageDisplayed = true; } diff --git a/Source/NETworkManager/Views/FirewallView.xaml.cs b/Source/NETworkManager/Views/FirewallView.xaml.cs index 037f561b81..776bbad65f 100644 --- a/Source/NETworkManager/Views/FirewallView.xaml.cs +++ b/Source/NETworkManager/Views/FirewallView.xaml.cs @@ -53,7 +53,7 @@ private void ViewModel_PropertyChanged(object sender, PropertyChangedEventArgs e } /// - /// Set data context for menus. + /// Updates the selection dependent menu items when a context menu is opened. /// /// /// diff --git a/Source/NETworkManager/Views/PortScannerHostView.xaml b/Source/NETworkManager/Views/PortScannerHostView.xaml index 8a820e112a..834c438ed3 100644 --- a/Source/NETworkManager/Views/PortScannerHostView.xaml +++ b/Source/NETworkManager/Views/PortScannerHostView.xaml @@ -23,6 +23,7 @@ + @@ -33,7 +34,8 @@ - + + diff --git a/Source/NETworkManager/Views/SNMPView.xaml b/Source/NETworkManager/Views/SNMPView.xaml index aecf87e9d0..1fb606ce28 100644 --- a/Source/NETworkManager/Views/SNMPView.xaml +++ b/Source/NETworkManager/Views/SNMPView.xaml @@ -102,9 +102,6 @@ x:Name="ComboBoxOid" ItemsSource="{Binding OidHistoryView}" IsReadOnly="{Binding IsRunning}"> - - -