是否可以在 WPF 应用程序中禁用 Windows 7 的自动窗口停靠功能?
11 回答
我最近需要对ResizeMode = CanResizeWithGrip
没有窗口装饰(没有标题栏和按钮)的自定义、可调整大小的 WPF 窗口执行此操作。我曾经DragMove()
移动窗口,当它被 AeroSnap 最大化时,窗口变得不可移动并因此锁定到位。
我尝试了Barn Monkey 的解决方案,它部分工作,但它仍会显示 AeroSnap 图形并将应用程序调整为全屏大小。我在下面对其进行了修改,现在它按预期工作:仍然可以调整大小,但根本没有 AeroSnap。
void Window1_MouseDown(object sender, MouseButtonEventArgs e)
{
if( e.LeftButton == MouseButtonState.Pressed )
{
// this prevents win7 aerosnap
if( this.ResizeMode != System.Windows.ResizeMode.NoResize )
{
this.ResizeMode = System.Windows.ResizeMode.NoResize;
this.UpdateLayout();
}
DragMove();
}
}
void Window1_MouseUp( object sender, MouseButtonEventArgs e )
{
if( this.ResizeMode == System.Windows.ResizeMode.NoResize )
{
// restore resize grips
this.ResizeMode = System.Windows.ResizeMode.CanResizeWithGrip;
this.UpdateLayout();
}
}
编辑:
自从我写这篇文章以来已经有一段时间了,但是由于人们仍然在看这个,我会用我现在使用的来更新它。我仍然使用基本相同的方法来防止边缘捕捉和移动我的窗口,但我现在将它们打包到自定义Behavior<>
类中,我可以附加到 aWindow
或UserControl
. 这使得它们非常容易与 MVVM 一起使用(我使用 Caliburn Micro)。
行为类是:
/// <summary>
/// behavior that makes a window/dialog draggable by clicking anywhere
/// on it that is not a control (ie, button)
/// </summary>
public class DragMoveBehavior<T> : Behavior<T> where T : FrameworkElement
{
protected override void OnAttached()
{
AssociatedObject.MouseLeftButtonDown += MouseDown;
base.OnAttached();
}
protected override void OnDetaching()
{
AssociatedObject.MouseLeftButtonDown -= MouseDown;
base.OnDetaching();
}
void MouseDown( object sender, EventArgs ea ) => Window.GetWindow( sender as T )?.DragMove();
}
public class WinDragMoveBehavior : DragMoveBehavior<Window> { }
public class UCDragMoveBehavior : DragMoveBehavior<UserControl> { }
/// <summary>
/// behavior that makes a window/dialog not resizable while clicked. this prevents
/// the window from being snapped to the edge of the screen (AeroSnap). if DragMoveBehavior
/// is also used, this must be attached first.
/// </summary>
/// <typeparam name="T"></typeparam>
public class NoSnapBehavior<T> : Behavior<T> where T : FrameworkElement
{
ResizeMode lastMode = ResizeMode.NoResize;
protected override void OnAttached()
{
AssociatedObject.MouseLeftButtonDown += MouseDown;
AssociatedObject.MouseLeftButtonUp += MouseUp;
base.OnAttached();
}
protected override void OnDetaching()
{
AssociatedObject.MouseLeftButtonDown -= MouseDown;
AssociatedObject.MouseLeftButtonUp -= MouseUp;
base.OnDetaching();
}
/// <summary>
/// make it so the window can be moved by dragging
/// </summary>
void MouseDown( object sender, EventArgs ea )
{
var win = Window.GetWindow( sender as T );
if( win != null && win.ResizeMode != ResizeMode.NoResize )
{
lastMode = win.ResizeMode;
win.ResizeMode = ResizeMode.NoResize;
win.UpdateLayout();
}
}
void MouseUp( object sender, EventArgs ea )
{
var win = Window.GetWindow( sender as T );
if( win != null && win.ResizeMode != lastMode )
{
win.ResizeMode = lastMode;
win.UpdateLayout();
}
}
}
public class WinNoSnapBehavior : NoSnapBehavior<Window> { }
public class UCNoSnapBehavior : NoSnapBehavior<UserControl> { }
然后我将它们附加到我的对话框视图中:
<UserControl ...
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
xmlns:util:="...">
<i:Interaction.Behaviors>
<util:UCNoSnapBehavior/>
<util:UCDragMoveBehavior/>
</i:Interaction.Behaviors>
...
</UserControl>
它只是工作!
如果您以 Win7 的“便签”为例,您可能已经注意到它没有标准的窗口边框。在此基础上,我只能告诉您,除了您ResizeMode="NoResize"
手动设置和处理调整大小行为之外,没有直接的方法可以做到这一点。以下是我快速创建的一个非常基本的非专业解决方案以帮助您入门,但如果您愿意,您可以附加更多功能:)
<Window
x:Class="WpfApplication1.Window1"
x:Name="window"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1"
Width="300"
Height="300"
ResizeMode="NoResize"
WindowStyle="None"
AllowsTransparency="True"
Background="Transparent"
WindowState="Maximized">
<Window.Resources>
<x:Array
x:Key="TextBlockList"
Type="{x:Type TextBlock}">
<TextBlock
Text="○ Resize Horizontally by dragging right grip" />
<TextBlock
Text="○ Resize Vertically by dragging bottom grip" />
<TextBlock
Text="○ Move Horizontally by dragging left grip" />
<TextBlock
Text="○ Move Verticallyby dragging top grip" />
</x:Array>
</Window.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition
Height="Auto" />
<RowDefinition
Height="{Binding Height, Mode=OneWay, ElementName=window}" />
<RowDefinition
Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition
Width="Auto" />
<ColumnDefinition
Width="{Binding Width, Mode=OneWay, ElementName=window}" />
<ColumnDefinition
Width="Auto" />
</Grid.ColumnDefinitions>
<GridSplitter
Grid.Column="1"
Grid.Row="1"
HorizontalAlignment="Left"
MinWidth="5" />
<GridSplitter
Grid.Column="1"
Grid.Row="1"
HorizontalAlignment="Right"
MinWidth="5" />
<GridSplitter
Grid.Column="1"
Grid.Row="1"
VerticalAlignment="Top"
MinHeight="5"
ResizeDirection="Rows"
HorizontalAlignment="Stretch" />
<GridSplitter
Grid.Column="1"
Grid.Row="1"
VerticalAlignment="Bottom"
MinHeight="5"
ResizeDirection="Rows"
HorizontalAlignment="Stretch" />
<Border
Grid.Column="1"
Grid.Row="1"
Background="{DynamicResource {x:Static SystemColors.WindowBrushKey}}"
Margin="5">
<Grid x:Name="root">
<ItemsControl
ItemsSource="{StaticResource TextBlockList}" />
</Grid>
</Border>
</Grid>
</Window>
您甚至可以制作一个可以在其父画布中调整大小和移动的控件(基本上是一个面板)。现在这个控件可以填充到一个透明的最大化窗口中。这会让您错觉您的控件是一个不响应“窗口捕捉”并且不会停靠的窗口!
希望这可以帮助。
问候,
米希尔·戈卡尼
我使用 anthony 的解决方案有一段时间了,但是如果你切换 ResizeMode 窗口会暂时删除尺寸边框,这有点烦人。这是另一个解决方案。通过设置 WS_OVERLAPPEDWINDOW 标志并删除 WS_THICKFRAME 标志将禁用窗口的 Aero Snap 功能,同时不会临时删除尺寸边框。您可以使用样式来获得所需的确切样式,但关键是删除 WS_THICKFRAME 标志。
public enum WindowStyles: int
{
WS_BORDER = 0x00800000,
WS_CAPTION = 0x00C00000,
WS_CHILD = 0x40000000,
WS_CHILDWINDOW = 0x40000000,
WS_CLIPCHILDREN = 0x02000000,
WS_CLIPSIBLINGS = 0x04000000,
WS_DISABLED = 0x08000000,
WS_DLGFRAME = 0x00400000,
WS_GROUP = 0x00020000,
WS_HSCROLL = 0x00100000,
WS_ICONIC = 0x20000000,
WS_MAXIMIZE = 0x01000000,
WS_MAXIMIZEBOX = 0x00010000,
WS_MINIMIZE = 0x20000000,
WS_MINIMIZEBOX = 0x00020000,
WS_OVERLAPPED = 0x00000000,
WS_OVERLAPPEDWINDOW = WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX,
WS_POPUP = unchecked((int)0x80000000),
WS_POPUPWINDOW = WS_POPUP | WS_BORDER | WS_SYSMENU,
WS_SIZEBOX = 0x00040000,
WS_SYSMENU = 0x00080000,
WS_TABSTOP = 0x00010000,
WS_THICKFRAME = 0x00040000,
WS_TILED = 0x00000000,
WS_TILEDWINDOW = WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX,
WS_VISIBLE = 0x10000000,
WS_VSCROLL = 0x00200000,
}
int newWinLongStyle = 0;
newWinLongStyle |= (int)WindowStyles.WS_OVERLAPPEDWINDOW;
newWinLongStyle ^= (int)WindowStyles.WS_THICKFRAME;
WindowInteropHelper helper = new WindowInteropHelper(this);
NativeMethods.SetWindowLong(helper.Handle, (int)WindowStyles.GWL_STYLE, newWinLongStyle);
这是我的解决方案。如果它们的 ResizeMode 设置为 ResizeMode.NoResize,Windows 将不会捕捉,因此,诀窍是可靠地确定拖动/移动何时开始和结束。
编辑: alexandrud 正确地指出,这仅适用于“无边界”窗口(在 WPF 术语中,WindowStyle = None)。
许多博萨人为给我们带来了这些信息而死。
class NoSnapWindow : System.Windows.Window
{
public NoSnapWindow()
{
SourceInitialized += delegate {
var source = HwndSource.FromVisual(this) as HwndSource;
source.AddHook(SourceHook);
};
}
private IntPtr SourceHook(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
{
switch (msg)
{
case 0x112: // WM_SYSCOMMAND
switch (wParam.ToIn32() & ~0x0F)
{
case 0xF010: // SC_MOVE
ResizeMode = ResizeMode.NoResize;
break;
}
break;
case 0x2A2: // WM_MOUSELEAVE
ResizeMode = ResizeMode.CanResize;
break;
}
return IntPtr.Zero;
}
}
我需要检测 Windows 7 Aero snaps/docks 以防止 WPF 应用程序上的窗口大小发生变化。在搜索过程中,我偶然发现了这篇文章,发现安东尼给出的答案非常有帮助。
以下是对我有用的。
private void DisplayWindow_MouseMove(object sender, MouseEventArgs e)
{
if (e.LeftButton == MouseButtonState.Released)
{
this.ResizeMode = System.Windows.ResizeMode.CanResizeWithGrip;
}
}
private void DisplayWindow_LocationChanged(object sender, EventArgs e)
{
this.ResizeMode = System.Windows.ResizeMode.NoResize;
}
窗口的 XAML 具有该ResizeMode="CanResizeWithGrip"
设置。
编辑:
我的回应是没有正确处理 Windows 7 Aero 快照。bjo 的回答优雅地为我解决了这个问题。
DragMove 挂起 UI 线程。该代码也有效。
void Window1_MouseDown(object sender, MouseButtonEventArgs e)
{
if( e.LeftButton == MouseButtonState.Pressed )
{
// this prevents win7 aerosnap
this.ResizeMode = System.Windows.ResizeMode.NoResize;
this.UpdateLayout();
DragMove();
// restore resize grips
this.ResizeMode = System.Windows.ResizeMode.CanResizeWithGrip;
this.UpdateLayout();
}
}
这对您来说可能不是完美的解决方案,但对我来说,将表单设置为不可调整大小就可以了。
我这里没有 Windows 7 盒子,所以我无法对此进行测试,但我会尝试以下方法:
1- 创建一个测试表单并覆盖 WndProc
2- 测试并记录与大小、位置和 WindowState 更改有关的特定消息。
3-确定停靠时发送到窗口的消息是否是大小/位置/窗口状态的组合,或者是否有另一个新的 Windows 7 消息(5 分钟的搜索没有向我透露任何信息。)
4- 一旦你有消息,检查是否有一个“独特”的情况正在发生。
5-修改您的代码以适应该独特情况。
如果没有其他人提出任何建议,我可能会在这个周末在家试一试。
我发现一个相当简单的解决方案也适用于无边框窗口:只需隐藏最大化按钮(如果由于缺少标题栏而尚未显示它的事件):
[DllImport("user32.dll")]
private static extern int GetWindowLong(IntPtr hWnd, int nIndex);
[DllImport("user32.dll")]
private static extern int SetWindowLong(IntPtr hWnd, int nIndex, int dwNewLong);
private const int GWL_STYLE = -16;
private const int WS_MAXIMIZEBOX = 0x10000;
private void Window_OnSourceInitialized(object sender, EventArgs e)
{
var hwnd = new WindowInteropHelper((Window)sender).Handle;
var value = GetWindowLong(hwnd, GWL_STYLE);
SetWindowLong(hwnd, GWL_STYLE, (int)(value & ~WS_MAXIMIZEBOX));
}
在 Windows 10 上设置“this.MaximizeBox = false”实际上会为我打破 Snap。也许这对你有用?
在控制面板的轻松访问中,选择
更容易专注于任务
并打勾
防止窗口移动到屏幕边缘时自动排列