diff --git a/AGENTS.md b/AGENTS.md
index 8cd0de9c93..342e79f0e2 100644
--- a/AGENTS.md
+++ b/AGENTS.md
@@ -92,7 +92,8 @@ REPOSITORY_ROOT/
- **DnsClient** - DNS lookups
- **IPNetwork2** - Network and subnet calculations
- **PSDiscoveryProtocol** - LLDP/CDP network discovery (PowerShell module embedded)
-- **AirspaceFixer** - WPF/WinForms interop fixes
+- **AirspaceFixer** - WPF/WinForms interop fixes (code embedded as `AirspacePanel` in `NETworkManager.Controls`,
+ based on AirspaceFixer and its fork AirspaceFixerDotNet)
- **log4net** - Logging framework
- **LiveChartsCore.SkiaSharpView.WPF** - Charts and graphs
- **Microsoft.Web.WebView2** - WebView2 control for web content
diff --git a/Source/NETworkManager.Controls/AirspacePanel.cs b/Source/NETworkManager.Controls/AirspacePanel.cs
new file mode 100644
index 0000000000..e03927cc18
--- /dev/null
+++ b/Source/NETworkManager.Controls/AirspacePanel.cs
@@ -0,0 +1,159 @@
+// Based on AirspaceFixer (https://github.com/chris84948/AirspaceFixer) and its fork AirspaceFixerDotNet
+// (https://github.com/ellipticbit/AirspaceFixerDotNet), adapted for NETworkManager.
+//
+// MIT License
+//
+// Copyright (c) 2017 chris84948
+// Copyright (c) 2024 EllipticBit, LLC
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all
+// copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+// SOFTWARE.
+
+using System;
+using System.Drawing.Imaging;
+using System.IO;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Media.Imaging;
+
+namespace NETworkManager.Controls;
+
+///
+/// Hosts content with embedded windows (e.g. Remote Desktop, PuTTY, WebView2), which are always drawn on top of
+/// WPF elements (airspace issue). While is , the content is
+/// replaced by a screenshot of it, so that WPF elements like dialogs can be shown above it.
+/// The template is defined in Resources/Styles/AirspacePanelStyles.xaml of the application.
+///
+[TemplatePart(Name = PartAirspaceContent, Type = typeof(ContentControl))]
+[TemplatePart(Name = PartAirspaceScreenshot, Type = typeof(Image))]
+public class AirspacePanel : ContentControl
+{
+ private const string PartAirspaceContent = "PART_AirspaceContent";
+ private const string PartAirspaceScreenshot = "PART_AirspaceScreenshot";
+
+ ///
+ /// Identifies the dependency property.
+ ///
+ public static readonly DependencyProperty FixAirspaceProperty = DependencyProperty.Register(nameof(FixAirspace),
+ typeof(bool), typeof(AirspacePanel), new FrameworkPropertyMetadata(false, OnFixAirspaceChanged));
+
+ private ContentControl _airspaceContent;
+ private Image _airspaceScreenshot;
+
+ ///
+ /// Replace the content with a screenshot of it (e.g. while a dialog is shown above the content).
+ ///
+ public bool FixAirspace
+ {
+ get => (bool)GetValue(FixAirspaceProperty);
+ set => SetValue(FixAirspaceProperty, value);
+ }
+
+ public override void OnApplyTemplate()
+ {
+ base.OnApplyTemplate();
+
+ _airspaceContent = GetTemplateChild(PartAirspaceContent) as ContentControl;
+ _airspaceScreenshot = GetTemplateChild(PartAirspaceScreenshot) as Image;
+ }
+
+ private static void OnFixAirspaceChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
+ {
+ if (d is AirspacePanel panel)
+ panel.UpdateAirspace((bool)e.NewValue);
+ }
+
+ private void UpdateAirspace(bool fix)
+ {
+ // Template not applied (yet)
+ if (_airspaceContent == null || _airspaceScreenshot == null)
+ return;
+
+ if (fix)
+ {
+ // Content not visible or not shown in a window - nothing to capture, keep the content
+ var screenshot = CreateScreenshotFromContent();
+
+ if (screenshot == null)
+ return;
+
+ // Show the screenshot in the size of the content when it was taken (top left aligned in the template), so
+ // it is not stretched / scaled if the window is resized while it is shown
+ _airspaceScreenshot.Width = _airspaceContent.ActualWidth;
+ _airspaceScreenshot.Height = _airspaceContent.ActualHeight;
+ _airspaceScreenshot.Source = screenshot;
+ _airspaceContent.Visibility = Visibility.Hidden;
+ _airspaceScreenshot.Visibility = Visibility.Visible;
+ }
+ else
+ {
+ // Always show the content again, even if the size changed in the meantime
+ _airspaceContent.Visibility = Visibility.Visible;
+ _airspaceScreenshot.Visibility = Visibility.Hidden;
+ _airspaceScreenshot.Source = null;
+ _airspaceScreenshot.ClearValue(WidthProperty);
+ _airspaceScreenshot.ClearValue(HeightProperty);
+ }
+ }
+
+ private BitmapSource CreateScreenshotFromContent()
+ {
+ var presentationSource = PresentationSource.FromVisual(_airspaceContent);
+
+ if (presentationSource?.CompositionTarget == null)
+ return null;
+
+ // The application is per monitor DPI aware (PerMonitorV2, see app.manifest): PointToScreen returns physical
+ // pixels, but the size is in device independent pixels and has to be scaled with the DPI of the monitor.
+ var transformToDevice = presentationSource.CompositionTarget.TransformToDevice;
+
+ var upperLeftPoint = _airspaceContent.PointToScreen(new Point(0, 0));
+ var width = (int)Math.Round(_airspaceContent.ActualWidth * transformToDevice.M11);
+ var height = (int)Math.Round(_airspaceContent.ActualHeight * transformToDevice.M22);
+
+ if (width <= 0 || height <= 0)
+ return null;
+
+ using var bitmap = new System.Drawing.Bitmap(width, height);
+
+ using (var graphics = System.Drawing.Graphics.FromImage(bitmap))
+ {
+ graphics.CopyFromScreen((int)Math.Round(upperLeftPoint.X), (int)Math.Round(upperLeftPoint.Y), 0, 0,
+ new System.Drawing.Size(width, height));
+ }
+
+ return GetBitmapSource(bitmap);
+ }
+
+ private static BitmapSource GetBitmapSource(System.Drawing.Bitmap bitmap)
+ {
+ using var memoryStream = new MemoryStream();
+
+ bitmap.Save(memoryStream, ImageFormat.Png);
+ memoryStream.Position = 0;
+
+ var bitmapImage = new BitmapImage();
+ bitmapImage.BeginInit();
+ bitmapImage.StreamSource = memoryStream;
+ bitmapImage.CacheOption = BitmapCacheOption.OnLoad;
+ bitmapImage.EndInit();
+ bitmapImage.Freeze();
+
+ return bitmapImage;
+ }
+}
diff --git a/Source/NETworkManager.Documentation/LibraryManager.cs b/Source/NETworkManager.Documentation/LibraryManager.cs
index 5844e094ac..91e034774a 100644
--- a/Source/NETworkManager.Documentation/LibraryManager.cs
+++ b/Source/NETworkManager.Documentation/LibraryManager.cs
@@ -28,6 +28,10 @@ public static class LibraryManager
Strings.Library_AirspaceFixer_Description,
Strings.License_MITLicense,
"https://github.com/chris84948/AirspaceFixer/blob/master/LICENSE"),
+ new("AirspaceFixerDotNet", "https://github.com/ellipticbit/AirspaceFixerDotNet",
+ Strings.Library_AirspaceFixer_Description,
+ Strings.License_MITLicense,
+ "https://github.com/ellipticbit/AirspaceFixerDotNet/blob/master/LICENSE"),
new("ControlzEx", "https://github.com/ControlzEx/ControlzEx",
Strings.Library_ControlzEx_Description,
Strings.License_MITLicense,
diff --git a/Source/NETworkManager.Documentation/Licenses/AirspaceFixerDotNet.txt b/Source/NETworkManager.Documentation/Licenses/AirspaceFixerDotNet.txt
new file mode 100644
index 0000000000..105eb6eb9b
--- /dev/null
+++ b/Source/NETworkManager.Documentation/Licenses/AirspaceFixerDotNet.txt
@@ -0,0 +1,22 @@
+MIT License
+
+Copyright (c) 2017 chris84948
+Copyright (c) 2024 EllipticBit, LLC
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
\ No newline at end of file
diff --git a/Source/NETworkManager.Documentation/NETworkManager.Documentation.csproj b/Source/NETworkManager.Documentation/NETworkManager.Documentation.csproj
index ce00c84acb..4b1ceece5c 100644
--- a/Source/NETworkManager.Documentation/NETworkManager.Documentation.csproj
+++ b/Source/NETworkManager.Documentation/NETworkManager.Documentation.csproj
@@ -17,6 +17,7 @@
+
@@ -48,6 +49,9 @@
PreserveNewest
+
+ PreserveNewest
+
PreserveNewest
diff --git a/Source/NETworkManager/App.xaml b/Source/NETworkManager/App.xaml
index 36c4e5454a..730d0e4469 100644
--- a/Source/NETworkManager/App.xaml
+++ b/Source/NETworkManager/App.xaml
@@ -24,6 +24,7 @@
+
diff --git a/Source/NETworkManager/MainWindow.xaml b/Source/NETworkManager/MainWindow.xaml
index 55261c4355..c844512895 100644
--- a/Source/NETworkManager/MainWindow.xaml
+++ b/Source/NETworkManager/MainWindow.xaml
@@ -10,7 +10,7 @@
xmlns:shell="clr-namespace:ControlzEx.Windows.Shell;assembly=ControlzEx"
xmlns:controls="clr-namespace:NETworkManager.Controls"
xmlns:interactivity="http://schemas.microsoft.com/xaml/behaviors"
- xmlns:asf="clr-namespace:AirspaceFixer;assembly=AirspaceFixer"
+ xmlns:networkManagerControls="clr-namespace:NETworkManager.Controls;assembly=NETworkManager.Controls"
xmlns:wpfHelpers="clr-namespace:NETworkManager.Utilities.WPF;assembly=NETworkManager.Utilities.WPF"
xmlns:localization="clr-namespace:NETworkManager.Localization.Resources;assembly=NETworkManager.Localization"
xmlns:settings="clr-namespace:NETworkManager.Settings;assembly=NETworkManager.Settings"
@@ -973,10 +973,10 @@
-
+
-
+
diff --git a/Source/NETworkManager/NETworkManager.csproj b/Source/NETworkManager/NETworkManager.csproj
index b711ec7b83..f7162f8073 100644
--- a/Source/NETworkManager/NETworkManager.csproj
+++ b/Source/NETworkManager/NETworkManager.csproj
@@ -61,7 +61,6 @@
-
diff --git a/Source/NETworkManager/Resources/Styles/AirspacePanelStyles.xaml b/Source/NETworkManager/Resources/Styles/AirspacePanelStyles.xaml
new file mode 100644
index 0000000000..7ff45d06fd
--- /dev/null
+++ b/Source/NETworkManager/Resources/Styles/AirspacePanelStyles.xaml
@@ -0,0 +1,29 @@
+
+
+
+
diff --git a/Website/docs/changelog/next-release.md b/Website/docs/changelog/next-release.md
index 60f2403fc7..82a2f50fff 100644
--- a/Website/docs/changelog/next-release.md
+++ b/Website/docs/changelog/next-release.md
@@ -189,6 +189,7 @@ Release date: **xx.xx.2026**
- Fixed the application list not scrolling to the selected application if it was opened from somewhere else (e.g. from the **Dashboard**, another application or the run command). [#3644](https://github.com/BornToBeRoot/NETworkManager/pull/3644)
- Fixed `Enter` in the application search (title bar) being able to trigger the default button of the shown tool (e.g. start a scan in the **IP Scanner**). It only filters the applications now. [#3660](https://github.com/BornToBeRoot/NETworkManager/pull/3660)
- Fixed columns that couldn't be made wider than the window (by dragging or double-clicking the gripper) in **About** and the DNS server profiles in **Settings > Network**. The grid scrolls horizontally now, the last (or main) column still fills the remaining width. [#3557](https://github.com/BornToBeRoot/NETworkManager/issues/3557)
+- Fixed the screenshot that is shown in place of embedded windows (**Remote Desktop**, **PowerShell**, **PuTTY**, **TigerVNC**, **Web Console**) while a dialog is open being cropped / stretched with a display scaling above 100% or on a monitor with a different scaling, and being stretched / scaled if the window is resized while the dialog is open (it keeps its size now). The `AirspacePanel` of [AirspaceFixer](https://github.com/chris84948/AirspaceFixer) is now part of NETworkManager, based on the fork [AirspaceFixerDotNet](https://github.com/ellipticbit/AirspaceFixerDotNet) with per monitor DPI support, which also fixes a GDI handle leak. [#3681](https://github.com/BornToBeRoot/NETworkManager/pull/3681)
**Firewall**