WIN11 Snap 是什么?自定义 WINDOW 如何使用 Snap 功能?
控件名:Snap
作 者:WPFDevelopersOrg - 驚鏵
原文链接[1]:https://github.com/WPFDevelopersOrg/WPFDevelopers
码云链接[2]:https://gitee.com/WPFDevelopersOrg/WPFDevelopers
框架支持.NET4 至 .NET8;Visual Studio 2022;Issue:Window 控件在 Win11 下不支持 Snap 功能[3]
Snap 是什么?Snap Layouts 是 Windows 11 的一项新功能,但是系统版本号必须 ≥ 21H2 。
Snap Layouts
Snap Layouts 提供 6 种不同的布局;双窗口排列 2 种三窗口排列 3 种四窗口排列 1 种using Microsoft.Windows.Shell;2. 修改 Window.xaml
using System;
using System.Windows;
using System.Windows.Automation.Peers;
using System.Windows.Automation.Provider;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Interop;
using System.Windows.Media;
using WPFDevelopers.Controls;
using WPFDevelopers.Core.Helpers;
using WPFDevelopers.Helpers;
namespaceWPFDevelopers.Net40
{
[TemplatePart(Name = TitleBarIcon, Type = typeof(Button))]
[TemplatePart(Name = HighTitleMaximizeButton, Type = typeof(Button))]
[TemplatePart(Name = HighTitleRestoreButton, Type = typeof(Button))]
[TemplatePart(Name = TitleBarMaximizeButton, Type = typeof(Button))]
[TemplatePart(Name = TitleBarRestoreButton, Type = typeof(Button))]
publicclassWindow : System.Windows.Window
{
privateconststring TitleBarIcon = "PART_TitleBarIcon";
privateconststring HighTitleMaximizeButton = "PART_MaximizeButton";
privateconststring HighTitleRestoreButton = "PART_RestoreButton";
privateconststring TitleBarMaximizeButton = "PART_TitleBarMaximizeButton";
privateconststring TitleBarRestoreButton = "PART_TitleBarRestoreButton";
private WindowStyle _windowStyle;
private Button _titleBarIcon;
private Button _highTitleMaximizeButton;
private Button _highTitleRestoreButton;
private Button _titleBarMaximizeButton;
private Button _titleBarRestoreButton;
private IntPtr hWnd;
publicstaticreadonly DependencyProperty TitleHeightProperty =
DependencyProperty.Register("TitleHeight", typeof(double), typeof(Window), new PropertyMetadata(50d));
publicstaticreadonly DependencyProperty NoChromeProperty =
DependencyProperty.Register("NoChrome", typeof(bool), typeof(Window), new PropertyMetadata(false));
publicstaticreadonly DependencyProperty TitleBarProperty =
DependencyProperty.Register("TitleBar", typeof(object), typeof(Window), new PropertyMetadata());
publicstaticreadonly DependencyProperty TitleBackgroundProperty =
DependencyProperty.Register("TitleBackground", typeof(Brush), typeof(Window), new PropertyMetadata());
publicstaticreadonly DependencyProperty TitleBarModeProperty =
DependencyProperty.Register("TitleBarMode", typeof(TitleBarMode), typeof(Window), new PropertyMetadata(TitleBarMode.Normal));
static Window()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(Window), new FrameworkPropertyMetadata(typeof(Window)));
}
public Window()
{
WPFDevelopers.Resources.ThemeChanged += Resources_ThemeChanged;
CommandBindings.Add(new CommandBinding(SystemCommands.CloseWindowCommand, CloseWindow));
CommandBindings.Add(new CommandBinding(SystemCommands.MaximizeWindowCommand, MaximizeWindow,
CanResizeWindow));
CommandBindings.Add(new CommandBinding(SystemCommands.MinimizeWindowCommand, MinimizeWindow,
CanMinimizeWindow));
CommandBindings.Add(new CommandBinding(SystemCommands.RestoreWindowCommand, RestoreWindow,
CanResizeWindow));
}
private void Resources_ThemeChanged(ThemeType currentTheme)
{
var isDark = currentTheme == ThemeType.Dark ? true : false;
var source = (HwndSource)PresentationSource.FromVisual(this);
Win32.EnableDarkModeForWindow(source, isDark);
}
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
_windowStyle = WindowStyle;
_titleBarIcon = GetTemplateChild(TitleBarIcon) as Button;
if (_titleBarIcon != )
{
_titleBarIcon.MouseDoubleClick -= Icon_MouseDoubleClick;
_titleBarIcon.MouseDoubleClick += Icon_MouseDoubleClick;
}
_highTitleMaximizeButton = GetTemplateChild(HighTitleMaximizeButton) as Button;
_highTitleRestoreButton = GetTemplateChild(HighTitleRestoreButton) as Button;
_titleBarMaximizeButton = GetTemplateChild(TitleBarMaximizeButton) as Button;
_titleBarRestoreButton = GetTemplateChild(TitleBarRestoreButton) as Button;
}
private void Icon_MouseDoubleClick(object sender, MouseButtonEventArgs e)
{
if (e.ChangedButton == MouseButton.Left)
Close();
}
publicdouble TitleHeight
{
get => (double)GetValue(TitleHeightProperty);
set => SetValue(TitleHeightProperty, value);
}
publicbool NoChrome
{
get => (bool)GetValue(NoChromeProperty);
set => SetValue(NoChromeProperty, value);
}
publicobject TitleBar
{
get => (object)GetValue(TitleBarProperty);
set => SetValue(TitleBarProperty, value);
}
public Brush TitleBackground
{
get => (Brush)GetValue(TitleBackgroundProperty);
set => SetValue(TitleBackgroundProperty, value);
}
public TitleBarMode TitleBarMode
{
get => (TitleBarMode)GetValue(TitleBarModeProperty);
set => SetValue(TitleBarModeProperty, value);
}
protected override void OnSourceInitialized(EventArgs e)
{
base.OnSourceInitialized(e);
hWnd = new WindowInteropHelper(this).Handle;
HwndSource.FromHwnd(hWnd).AddHook(WindowProc);
if (TitleBarMode == TitleBarMode.Normal)
TitleHeight = SystemParameters2.Current.WindowNonClientFrameThickness.Top;
}
protected override void OnContentRendered(EventArgs e)
{
base.OnContentRendered(e);
if (SizeToContent == SizeToContent.WidthAndHeight)
InvalidateMeasure();
}
#region Window Commands
private void CanResizeWindow(object sender, CanExecuteRoutedEventArgs e)
{
e.CanExecute = ResizeMode == ResizeMode.CanResize || ResizeMode == ResizeMode.CanResizeWithGrip;
}
private void CanMinimizeWindow(object sender, CanExecuteRoutedEventArgs e)
{
e.CanExecute = ResizeMode != ResizeMode.NoResize;
}
private void CloseWindow(object sender, ExecutedRoutedEventArgs e)
{
SystemCommands.CloseWindow(this);
}
private void MaximizeWindow(object sender, ExecutedRoutedEventArgs e)
{
if (WindowState == WindowState.Normal)
{
WindowStyle = WindowStyle.SingleBorderWindow;
WindowState = WindowState.Maximized;
WindowStyle = WindowStyle.;
}
}
private void MinimizeWindow(object sender, ExecutedRoutedEventArgs e)
{
Win32.SendMessage(hWnd, WindowsMessageCodes.WM_SYSCOMMAND, new IntPtr(WindowsMessageCodes.SC_MINIMIZE), IntPtr.Zero);
}
private void RestoreWindow(object sender, ExecutedRoutedEventArgs e)
{
SystemCommands.RestoreWindow(this);
}
private IntPtr WindowProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
{
switch (msg)
{
case WindowsMessageCodes.WM_SYSCOMMAND:
if (wParam.ToInt32() == WindowsMessageCodes.SC_MINIMIZE)
{
_windowStyle = WindowStyle;
if (WindowStyle != WindowStyle.SingleBorderWindow)
WindowStyle = WindowStyle.SingleBorderWindow;
WindowState = WindowState.Minimized;
handled = true;
}
elseif (wParam.ToInt32() == WindowsMessageCodes.SC_RESTORE)
{
WindowState = WindowState.Normal;
WindowStyle = WindowStyle.;
if (WindowStyle. != _windowStyle)
WindowStyle = _windowStyle;
handled = true;
}
break;
case WindowsMessageCodes.WM_NCHITTEST:
case WindowsMessageCodes.WM_NCLBUTTONDOWN:
try
{
if (!OSVersionHelper.IsSnapLayoutSupported()
||
ResizeMode == ResizeMode.NoResize
||
ResizeMode == ResizeMode.CanMinimize)
break;
else
{
IntPtr result = IntPtr.Zero;
if (HandleSnapLayoutMessage(msg, lParam, ref result))
{
handled = true;
return result;
}
}
}
catch (OverflowException)
{
handled = true;
}
break;
}
return IntPtr.Zero;
}
private bool HandleSnapLayoutMessage(int msg, IntPtr lParam, ref IntPtr result)
{
Button button = TitleBarMode == TitleBarMode.Normal
? (WindowState != WindowState.Maximized ? _titleBarMaximizeButton : _titleBarRestoreButton)
: (WindowState != WindowState.Maximized ? _highTitleMaximizeButton : _highTitleRestoreButton);
if (button == || button.ActualWidth <= 0 || button.ActualHeight <= 0)
returnfalse;
var contentPresenter = button.Template.FindName("PART_ContentPresenter", button) as ContentPresenter;
var x = lParam.ToInt32() & 0xffff;
var y = lParam.ToInt32() >> 16;
var dpiX = OSVersionHelper.DeviceUnitsScalingFactorX;
var rect = new Rect(button.PointToScreen(new Point()), new Size(button.ActualWidth * dpiX, button.ActualHeight * dpiX));
var point = new Point(x, y);
if (msg == WindowsMessageCodes.WM_NCHITTEST && contentPresenter != )
{
if (!rect.Contains(point))
{
if(contentPresenter.Opacity != 0.7)
contentPresenter.Opacity = 0.7;
returnfalse;
}
contentPresenter.Opacity = 1;
result = new IntPtr(OSVersionHelper.HTMAXBUTTON);
}
elseif (msg == WindowsMessageCodes.WM_NCLBUTTONDOWN)
{
IInvokeProvider invokeProv = new ButtonAutomationPeer(button).GetPattern(PatternInterface.Invoke) as IInvokeProvider;
invokeProv?.Invoke();
}
returntrue;
}
private void ShowSystemMenu(object sender, ExecutedRoutedEventArgs e)
{
var element = e.OriginalSource as FrameworkElement;
if (element == )
return;
var point = WindowState == WindowState.Maximized
? new Point(0. element.ActualHeight)
: new Point(Left + BorderThickness.Left, element.ActualHeight + Top + BorderThickness.Top);
point = element.TransformToAncestor(this).Transform(point);
SystemCommands.ShowSystemMenu(this, point);
}
#endregion
}
}
<Style BasedOn="{x:}" TargetType="{x:Type wd:Window}">3. 新增 OSVersionHelper.cs构造函数获取 DPI 信息;IsSnapLayoutSupported() 方法用于判断系统是否支持 Snap 布局;获取 Windows 版本号;
<Setter Property="Foreground" Value="{DynamicResource WD.RegularTextBrush}" />
<Setter Property="Background" Value="{DynamicResource WD.BackgroundBrush}" />
<Setter Property="BorderBrush" Value="{DynamicResource WD.WindowBorderBrush}" />
<Setter Property="TitleBackground" Value="{DynamicResource WD.WindowBorderBrush}" />
<Setter Property="IsTabStop" Value="False" />
<Setter Property="BorderThickness" Value="1" />
<Setter Property="SnapsToDevicePixels" Value="True" />
<Setter Property="UseLayoutRounding" Value="True" />
<Setter Property="TextOptions.TextFormattingMode" Value="Ideal" />
<Setter Property="WindowStyle" Value="" />
<Setter Property="FontFamily" Value="{DynamicResource WD.FontFamily}" />
<Setter Property="shell:WindowChrome.WindowChrome">
<Setter.Value>
<shell:WindowChrome CaptionHeight="{Binding TitleHeight, RelativeSource={RelativeSource AncestorType=wd:Window}}" GlassFrameThickness="0.0.0..1" />
</Setter.Value>
</Setter>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type wd:Window}">
<Border
Name="PART_Border"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
SnapsToDevicePixels="True">
<Grid x:Name="LayoutRoot">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<control:SmallPanel
x:Name="PART_Normal"
Grid.Row="0"
Background="{TemplateBinding TitleBackground}">
<Grid Height="{TemplateBinding TitleHeight}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<StackPanel Orientation="Horizontal">
<Button
x:Name="PART_TitleBarIcon"
Margin="5,0.0.0"
VerticalAlignment="Center"
shell:WindowChrome.IsHitTestVisibleInChrome="True"
Background="Transparent"
BorderThickness="0"
Visibility="{Binding Icon, RelativeSource={RelativeSource Mode=TemplatedParent}, Converter={StaticResource ObjectToVisibilityConverter}}">
<Image
Width="{x:Static SystemParameters.SmallIconWidth}"
Height="{x:Static SystemParameters.SmallIconHeight}"
IsHitTestVisible="False"
RenderOptions.BitmapScalingMode="HighQuality"
Source="{TemplateBinding Icon}" />
</Button>
<ContentControl
Margin="5,0.0.0"
VerticalAlignment="Center"
Content="{TemplateBinding Title}"
FontSize="{DynamicResource {x:Static SystemFonts.CaptionFontSizeKey}}"
Foreground="{DynamicResource WD.WindowTextBrush}"
IsTabStop="False" />
</StackPanel>
<StackPanel
Grid.Column="1"
HorizontalAlignment="Right"
VerticalAlignment="Top"
shell:WindowChrome.IsHitTestVisibleInChrome="True"
Orientation="Horizontal">
<StackPanel x:Name="PART_TitleBarMinAndMax" Orientation="Horizontal">
<Button
x:Name="PART_TitleBarMinimizeButton"
Padding="0"
Command="{Binding Source={x:Static shell:SystemCommands.MinimizeWindowCommand}}"
IsTabStop="False"
ToolTip="{Binding [Minimize], Source={x:Static resx:LanguageManager.Instance}}">
<Grid HorizontalAlignment="Center" VerticalAlignment="Center">
<Rectangle
Width="10"
Height="1"
Margin="0.7,0.0"
VerticalAlignment="Bottom"
Fill="{DynamicResource WD.WindowTextBrush}" />
</Grid>
</Button>
<Button
x:Name="PART_TitleBarMaximizeButton"
Padding="0"
Command="{Binding Source={x:Static shell:SystemCommands.MaximizeWindowCommand}}"
IsTabStop="False"
ToolTip="{Binding [Maximize], Source={x:Static resx:LanguageManager.Instance}}">
<Grid HorizontalAlignment="Center" VerticalAlignment="Center">
<Path
Width="10"
Height="10"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Data="{DynamicResource WD.WindowMaximizeGeometry}"
Fill="{DynamicResource WD.WindowTextBrush}"
Stretch="Uniform"
UseLayoutRounding="False" />
</Grid>
</Button>
<Button
x:Name="PART_TitleBarRestoreButton"
Padding="0"
Command="{Binding Source={x:Static shell:SystemCommands.RestoreWindowCommand}}"
IsTabStop="False"
ToolTip="{Binding [Restore], Source={x:Static resx:LanguageManager.Instance}}"
Visibility="Collapsed">
<Grid HorizontalAlignment="Center" VerticalAlignment="Center">
<Path
Width="10"
Height="10"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Data="{DynamicResource WD.WindowRestoreGeometry}"
Fill="{DynamicResource WD.WindowTextBrush}"
Stretch="Uniform"
UseLayoutRounding="False" />
</Grid>
</Button>
</StackPanel>
<Button
Name="PART_TitleBarCloseButton"
Command="{Binding Source={x:Static shell:SystemCommands.CloseWindowCommand}}"
IsTabStop="False"
ToolTip="{Binding [Close], Source={x:Static resx:LanguageManager.Instance}}">
<Path
Width="10"
Height="10"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Data="{DynamicResource WD.WindowCloseGeometry}"
Fill="{DynamicResource WD.WindowTextBrush}"
Stretch="Uniform" />
</Button>
</StackPanel>
</Grid>
</control:SmallPanel>
<control:SmallPanel
x:Name="PART_HighTitleBar"
Grid.Row="0"
Background="{TemplateBinding TitleBackground}"
Visibility="Collapsed">
<Grid
x:Name="PART_GridChrome"
Height="{TemplateBinding TitleHeight}"
Margin="10.0.0.0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" MinWidth="30" />
</Grid.ColumnDefinitions>
<Image
Width="23"
Height="23"
VerticalAlignment="Center"
RenderOptions.BitmapScalingMode="HighQuality"
Source="{TemplateBinding Icon}"
Visibility="{TemplateBinding Icon,
Converter={StaticResource ObjectToVisibilityConverter}}" />
<TextBlock
x:Name="PART_Title"
Grid.Column="1"
Margin="6,0"
VerticalAlignment="Center"
FontSize="{DynamicResource WD.TitleFontSize}"
Foreground="{DynamicResource WD.WindowTextBrush}"
Text="{TemplateBinding Title}" />
<StackPanel
Grid.Column="2"
Margin="0.6"
shell:WindowChrome.IsHitTestVisibleInChrome="True"
Orientation="Horizontal">
<StackPanel x:Name="PART_MinAndMax" Orientation="Horizontal">
<Button
Name="PART_MinimizeButton"
Padding="0"
Command="{Binding Source={x:Static shell:SystemCommands.MinimizeWindowCommand}}"
IsTabStop="False"
ToolTip="{Binding [Minimize], Source={x:Static resx:LanguageManager.Instance}}">
<Grid HorizontalAlignment="Center" VerticalAlignment="Center">
<Rectangle
x:Name="MinimizeGlyph"
Width="10"
Height="1"
Margin="0.7,0.0"
VerticalAlignment="Bottom"
Fill="{DynamicResource WD.WindowTextBrush}" />
</Grid>
</Button>
<Button
x:Name="PART_MaximizeButton"
Padding="0"
Command="{Binding Source={x:Static shell:SystemCommands.MaximizeWindowCommand}}"
IsTabStop="False"
ToolTip="{Binding [Maximize], Source={x:Static resx:LanguageManager.Instance}}">
<Grid HorizontalAlignment="Center" VerticalAlignment="Center">
<Path
Width="10"
Height="10"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Data="{DynamicResource WD.WindowMaximizeGeometry}"
Fill="{DynamicResource WD.WindowTextBrush}"
Stretch="Uniform"
UseLayoutRounding="False" />
</Grid>
</Button>
<Button
x:Name="PART_RestoreButton"
Padding="0"
Command="{Binding Source={x:Static shell:SystemCommands.RestoreWindowCommand}}"
IsTabStop="False"
ToolTip="{Binding [Restore], Source={x:Static resx:LanguageManager.Instance}}"
Visibility="Collapsed">
<Grid HorizontalAlignment="Center" VerticalAlignment="Center">
<Path
Width="10"
Height="10"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Data="{DynamicResource WD.WindowRestoreGeometry}"
Fill="{DynamicResource WD.WindowTextBrush}"
Stretch="Uniform"
UseLayoutRounding="False" />
</Grid>
</Button>
</StackPanel>
<Button
Name="PART_CloseButton"
Command="{Binding Source={x:Static shell:SystemCommands.CloseWindowCommand}}"
IsTabStop="False"
ToolTip="{Binding [Close], Source={x:Static resx:LanguageManager.Instance}}">
<Path
Width="10"
Height="10"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Data="{DynamicResource WD.WindowCloseGeometry}"
Fill="{DynamicResource WD.WindowTextBrush}"
Stretch="Uniform" />
</Button>
</StackPanel>
</Grid>
<ContentPresenter
x:Name="PART_TitleToolBar"
shell:WindowChrome.IsHitTestVisibleInChrome="True"
Content="{Binding TitleBar, RelativeSource={RelativeSource TemplatedParent}, Mode=OneWay}"
Focusable="False"
Visibility="Collapsed" />
</control:SmallPanel>
<AdornerDecorator Grid.Row="1" KeyboardNavigation.IsTabStop="False">
<ContentPresenter x:Name="MainContentPresenter" ClipToBounds="True" />
</AdornerDecorator>
<ResizeGrip
x:Name="ResizeGrip"
Grid.Row="1"
HorizontalAlignment="Right"
VerticalAlignment="Bottom"
IsTabStop="False"
Visibility="Collapsed" />
</Grid>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="TitleBarMode" Value="HighTitleBar">
<Setter TargetName="PART_HighTitleBar" Property="Visibility" Value="Visible" />
<Setter TargetName="PART_Normal" Property="Visibility" Value="Collapsed" />
</Trigger>
<Trigger Property="WindowState" Value="Maximized">
<Setter TargetName="PART_RestoreButton" Property="Visibility" Value="Visible" />
<Setter TargetName="PART_MaximizeButton" Property="Visibility" Value="Collapsed" />
<Setter TargetName="PART_TitleBarRestoreButton" Property="Visibility" Value="Visible" />
<Setter TargetName="PART_TitleBarMaximizeButton" Property="Visibility" Value="Collapsed" />
<Setter TargetName="PART_Border" Property="Margin" Value="7" />
</Trigger>
<Trigger Property="WindowStyle" Value="ToolWindow">
<Setter TargetName="PART_MinAndMax" Property="Visibility" Value="Collapsed" />
<Setter TargetName="PART_TitleBarMinAndMax" Property="Visibility" Value="Collapsed" />
</Trigger>
<Trigger Property="NoChrome" Value="True">
<Setter TargetName="PART_GridChrome" Property="Visibility" Value="Collapsed" />
<Setter TargetName="PART_TitleToolBar" Property="Visibility" Value="Visible" />
</Trigger>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="ResizeMode" Value="CanResizeWithGrip" />
<Condition Property="WindowState" Value="Normal" />
</MultiTrigger.Conditions>
<Setter TargetName="ResizeGrip" Property="Visibility" Value="Visible" />
</MultiTrigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
using Microsoft.Win32;
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Interop;
using System.Windows.Media;
using WPFDevelopers.Helpers;
namespaceWPFDevelopers.Core.Helpers
{
publicclassOSVersionHelper
{
privateconstdouble LogicalDpi = 96.0;
publicstatic MatrixTransform TransformFromDevice { get; }
publicstatic MatrixTransform TransformToDevice { get; }
publicstaticdouble DeviceDpiX { get; }
publicstaticdouble DeviceDpiY { get; }
publicstaticdouble DeviceUnitsScalingFactorX => TransformToDevice.Matrix.M11;
publicstaticdouble DeviceUnitsScalingFactorY => TransformToDevice.Matrix.M22;
privatestaticbool? _isSnapLayoutSupported;
public static bool IsSnapLayoutSupported()
{
if (_isSnapLayoutSupported.HasValue)
{
return _isSnapLayoutSupported.Value;
}
_isSnapLayoutSupported = CheckSnapLayoutSupport();
return _isSnapLayoutSupported.Value;
}
static OSVersionHelper()
{
var dC = Win32.GetDC(IntPtr.Zero);
if (dC != IntPtr.Zero)
{
constint logicPixelsX = 88;
constint logicPixelsY = 90;
DeviceDpiX = Win32.GetDeviceCaps(dC, logicPixelsX);
DeviceDpiY = Win32.GetDeviceCaps(dC, logicPixelsY);
Win32.ReleaseDC(IntPtr.Zero, dC);
}
else
{
DeviceDpiX = LogicalDpi;
DeviceDpiY = LogicalDpi;
}
var identity = Matrix.Identity;
var identity2 = Matrix.Identity;
identity.Scale(DeviceDpiX / LogicalDpi, DeviceDpiY / LogicalDpi);
identity2.Scale(LogicalDpi / DeviceDpiX, LogicalDpi / DeviceDpiY);
TransformFromDevice = new MatrixTransform(identity2);
TransformFromDevice.Freeze();
TransformToDevice = new MatrixTransform(identity);
TransformToDevice.Freeze();
}
private static bool CheckSnapLayoutSupport()
{
var version = GetWindowsBuildNumber();
return version >= 22000;
}
private static int GetWindowsBuildNumber()
{
try
{
using (RegistryKey key = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Microsoft\Windows NT\CurrentVersion"))
{
if (key != )
{
var buildNumber = key.GetValue("CurrentBuildNumber");
if (buildNumber != && int.TryParse(buildNumber.ToString(), outint result))
{
return result;
}
}
}
}
catch
{
}
return0;
}
#region SnapLayout
privateconstdouble DPI_SCALE = 1.5;
publicconstint HTMAXBUTTON = 9;
private static HwndSource GetWindowHwndSource(DependencyObject dependencyObject)
{
if (dependencyObject is Window window)
{
return PresentationSource.FromVisual(window) as HwndSource;
}
elseif (dependencyObject is ToolTip tooltip)
{
return PresentationSource.FromVisual(tooltip) as HwndSource;
}
elseif (dependencyObject is Popup popup)
{
if (popup.Child is)
return;
return PresentationSource.FromVisual(popup.Child) as HwndSource;
}
elseif (dependencyObject is Visual visual)
{
return PresentationSource.FromVisual(visual) as HwndSource;
}
return;
}
#endregion
}
}
GitHub 源码地址[4]
Gitee 源码地址[5]
参考资料[1]原文链接: https://github.com/WPFDevelopersOrg/WPFDevelopers
[2]码云链接: https://gitee.com/WPFDevelopersOrg/WPFDevelopers
[3]Window 控件在 Win11 下不支持 Snap 功能: https://github.com/WPFDevelopersOrg/WPFDevelopers/issues/162
[4]GitHub 源码地址: https://github.com/WPFDevelopersOrg/WPFDevelopers/blob/dev/src/WPFDevelopers.Net40/Window.cs
[5]Gitee 源码地址: https://gitee.com/WPFDevelopersOrg/WPFDevelopers/blob/dev/src/WPFDevelopers.Net40/Window.cs
相关教程
2025-04-16
2025-05-13
2024-08-18
2024-06-17
2024-03-19
2025-04-16
2024-02-21
2024-01-21
2023-11-06
2025-06-16
2025-06-13
2025-06-12
2025-06-11
2025-06-11
copyright © 2012-2025 雪花家园 m.xhjaty.com 版权声明