Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
159 changes: 159 additions & 0 deletions Source/NETworkManager.Controls/AirspacePanel.cs
Original file line number Diff line number Diff line change
@@ -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;

/// <summary>
/// Hosts content with embedded windows (e.g. Remote Desktop, PuTTY, WebView2), which are always drawn on top of
/// WPF elements (airspace issue). While <see cref="FixAirspace" /> is <see langword="true" />, 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 <c>Resources/Styles/AirspacePanelStyles.xaml</c> of the application.
/// </summary>
[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";

/// <summary>
/// Identifies the <see cref="FixAirspace" /> dependency property.
/// </summary>
public static readonly DependencyProperty FixAirspaceProperty = DependencyProperty.Register(nameof(FixAirspace),
typeof(bool), typeof(AirspacePanel), new FrameworkPropertyMetadata(false, OnFixAirspaceChanged));

private ContentControl _airspaceContent;
private Image _airspaceScreenshot;

/// <summary>
/// Replace the content with a screenshot of it (e.g. while a dialog is shown above the content).
/// </summary>
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;
}
}
4 changes: 4 additions & 0 deletions Source/NETworkManager.Documentation/LibraryManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
Original file line number Diff line number Diff line change
@@ -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.
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
</PropertyGroup>
<ItemGroup>
<None Remove="Licenses\AirspaceFixer.txt" />
<None Remove="Licenses\AirspaceFixerDotNet.txt" />
<None Remove="Licenses\ControlzEx.txt" />
<None Remove="Licenses\DnsClient.NET.txt" />
<None Remove="Licenses\Dragablz.txt" />
Expand Down Expand Up @@ -48,6 +49,9 @@
<Content Include="Licenses\AirspaceFixer.txt">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="Licenses\AirspaceFixerDotNet.txt">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="Licenses\ControlzEx.txt">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
Expand Down
1 change: 1 addition & 0 deletions Source/NETworkManager/App.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
<!-- Control templates (import before styles) -->
<ResourceDictionary Source="/Resources/Templates/ValidationErrorTemplates.xaml" />
<!-- Styles -->
<ResourceDictionary Source="/Resources/Styles/AirspacePanelStyles.xaml" />
<ResourceDictionary Source="/Resources/Styles/ButtonStyles.xaml" />
<ResourceDictionary Source="/Resources/Styles/CheckBoxStyles.xaml" />
<ResourceDictionary Source="/Resources/Styles/ChildWindowStyles.xaml" />
Expand Down
8 changes: 4 additions & 4 deletions Source/NETworkManager/MainWindow.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -973,10 +973,10 @@
</interactivity:Interaction.Triggers>
</Grid>
</Border>
<asf:AirspacePanel Grid.Column="1" Grid.Row="0"
FixAirspace="{Binding Source={x:Static settings:ConfigurationManager.Current}, Path=FixAirspace}">
<networkManagerControls:AirspacePanel Grid.Column="1" Grid.Row="0"
FixAirspace="{Binding Source={x:Static settings:ConfigurationManager.Current}, Path=FixAirspace}">
<ContentControl x:Name="ContentControlApplication" />
</asf:AirspacePanel>
</networkManagerControls:AirspacePanel>
</Grid>
<Grid>
<Grid.Style>
Expand Down
1 change: 0 additions & 1 deletion Source/NETworkManager/NETworkManager.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,6 @@
</Content>
</ItemGroup>
<ItemGroup>
<PackageReference Include="AirspaceFixer" Version="1.0.6" />
<PackageReference Include="DnsClient" Version="1.8.0" />
<PackageReference Include="gong-wpf-dragdrop" Version="4.0.0" />
<PackageReference Include="IPNetwork2" Version="4.3.0" />
Expand Down
29 changes: 29 additions & 0 deletions Source/NETworkManager/Resources/Styles/AirspacePanelStyles.xaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
ο»Ώ<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:controls="clr-namespace:NETworkManager.Controls;assembly=NETworkManager.Controls">
<!--
Based on AirspaceFixer (https://github.com/chris84948/AirspaceFixer) and its fork AirspaceFixerDotNet
(https://github.com/ellipticbit/AirspaceFixerDotNet), MIT License.
Copyright (c) 2017 chris84948, Copyright (c) 2024 EllipticBit, LLC - see NETworkManager.Controls/AirspacePanel.cs
-->
<Style TargetType="{x:Type controls:AirspacePanel}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type controls:AirspacePanel}">
<!-- ClipToBounds: The screenshot keeps its size if the window is made smaller while it is shown -->
<Grid HorizontalAlignment="{TemplateBinding HorizontalAlignment}"
VerticalAlignment="{TemplateBinding VerticalAlignment}"
ClipToBounds="True">
<!-- Size is set in code (size of the content when the screenshot was taken), not stretched -->
<Image x:Name="PART_AirspaceScreenshot"
HorizontalAlignment="Left" VerticalAlignment="Top"
Stretch="Fill"
Visibility="Hidden" />
<ContentControl x:Name="PART_AirspaceContent"
Content="{TemplateBinding Content}" />
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
1 change: 1 addition & 0 deletions Website/docs/changelog/next-release.md
Original file line number Diff line number Diff line change
Expand Up @@ -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**

Expand Down
Loading