当前位置: 雪花家园 >  系统资讯 >  WIN11 Snap 是什么?自定义 WINDOW 如何使用 Snap 功能?快速学习WIN11 Snap功能及操作步骤

WIN11 Snap 是什么?自定义 WINDOW 如何使用 Snap 功能?快速学习WIN11 Snap功能及操作步骤

更新时间:2025-06-17 11:22:40作者:xhjaty

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 种WIN11 Snap 是什么?自定义 WINDOW 如何使用 Snap 功能?快速学习WIN11 Snap功能及操作步骤提供了多种预设的窗口布局,可以将窗口快速调整到屏幕的左侧、右侧、上方、下方,甚至是四分之一屏幕区域;可以通过将窗口拖动到屏幕边缘来启动 Snap,或使用快捷键(Win + 左/右箭头、Win + 上/下箭头)来实现;在应用程序或窗口中按下「Win + Z」快捷键,或是将鼠标光标悬停在窗口最大化按钮上以显示 Snap Layout 菜单。分屏允许通过将屏幕的一半或四分之一,让多任务处理变得更加流畅,对于大屏幕或多显示器的用户尤其有用;如何在 WPF 中自定义 Window 支持 SnapLayout 功能1. 修改 Window.cs重写 OnSourceInitialized,实现 Win32 消息钩子 HwndSourceHook;WM_NCHITTEST:用于检测鼠标在 Window 的哪个区域;WM_NCLBUTTONDOWN:鼠标左键按下,是否在标题栏按钮;处理 Snap Layout 消息 HandleSnapLayoutMessage;获取当前最大化还是还原按钮;通过 lParam 获取鼠标屏幕坐标,是否在最大化或者还原按钮上;然后返回 HTMAXBUTTON 通知系统鼠标现在在最大化按钮上;
using Microsoft.Windows.Shell;
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
}
}
2. 修改 Window.xaml
<Style BasedOn="{x:}" TargetType="{x:Type wd:Window}">
<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>
3. 新增 OSVersionHelper.cs构造函数获取 DPI 信息;IsSnapLayoutSupported() 方法用于判断系统是否支持 Snap 布局;获取 Windows 版本号;
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
}
}

WIN11 Snap 是什么?自定义 WINDOW 如何使用 Snap 功能?快速学习WIN11 Snap功能及操作步骤

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

相关教程

copyright ©  2012-2025 雪花家园 m.xhjaty.com 版权声明