如何在应用程序中禁用Aero Snap?

10

在WPF应用程序中,是否有可能禁用Windows 7的自动窗口停靠功能?


1
你是想通过编程来关闭整个系统,还是只是单独关闭一个窗口? - Jacob G
1
我只想关闭我的应用程序窗口,而不是整个系统。 - hans
不可能。这是一个全有或全无的命题。我会尝试修改我的表单以防止调整大小。(我这里没有Win7系统,所以无法测试) - Jacob G
但例如Windows7的笔记软件的窗口不能停靠,但可以调整它们的大小。 - hans
1
我建议不要尝试这个。我使用的另一个程序是 Aero Proof(我认为它不是 .NET),它真的很烦人。 - RCIX
12个回答

19

我最近需要对自定义的可调整大小的 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<>类,我可以将其附加到WindowUserControl上。这使它们非常容易与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>

它只是有效!


太棒了!正是我遇到的问题,这个解决方案复制粘贴后就能立即运行;D - StinkyCat
代码的修改对我来说很好,但是花了一段时间。在我的第一次尝试中,MouseUp从未被触发。这是因为我在窗口上使用了ControlTemplate,并且该模板的主要成员是一个简单的<Grid>。只有在我将其修改为<Grid Background="#01000000">之后,所有东西都变得圆润起来了。 - Jan Zeman
@JanZeman,是的,事件处理程序在根窗口上,因此如果有任何子控件拦截用户输入,则事件将无法到达窗口。此外,如果控件/窗口设置为透明,则鼠标事件将通过它传递(不会在其上触发任何事件)。 - bj0
@bj0,请在 <UserControl ... 正下方的 xmlns 声明中修复拼写错误 utli - vullnetyy

5

我曾经使用过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);

5

如果你正在举例说明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>

你甚至可以制作一个控件(基本上是一个面板),它可以在其父画布中调整大小并移动。现在,此控件可以填充到透明的最大化窗口中。这将使你产生一种控件就像是一个不响应'窗口捕捉'且不会停靠的窗口的错觉!
希望这能有所帮助。 谢谢, Mihir Gokani

3
这是我的解决方案。如果将窗口的ResizeMode设置为ResizeMode.NoResize,则Windows将无法捕捉,因此,关键是可靠地确定拖动/移动何时开始和结束。
编辑:alexandrud正确指出,这仅适用于“无边框”窗口(在WPF术语中为WindowStyle = None)。
许多Bothans死了,才带来了这些信息。
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;
    }
}

1
这个不起作用。当你拖动窗口时,窗口的边框会消失,而且再也不会出现了。 - Alexandru Dicu
啊,谢谢你指出这个问题。我一直在使用无边框窗口的代码,所以从来没有注意到这个问题。 - anthony

3

我需要检测 Windows 7 Aero snaps/docks,以防止 WPF 应用程序的窗口大小更改。在我的搜索过程中,我偶然发现了这篇文章,并发现Anthony给出的答案非常有帮助。

以下是对我有用的内容:

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的回应 巧妙地解决了我的问题。


1

对于你来说可能不是完美的解决方案,但对我来说将表单设置为不可调整大小就可以解决问题。


1

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();
    }
}

0
我发现一个相当简单的解决方案,适用于无边框窗口:只需隐藏最大化按钮(即使由于缺少标题栏而已经不显示):
    [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));
    }

0

我这里没有Windows 7的电脑,所以无法测试,但以下是我的建议:

1- 创建一个测试表单并覆盖WndProc
2- 测试和记录与大小、位置和窗口状态更改相关的特定消息。
3- 确定当停靠时发送到窗口的消息是否是大小/位置/窗口状态的组合,还是是否有另一种新的Windows 7消息(5分钟的搜索没有给我带来任何发现)。
4- 一旦您获得了消息,请检查是否存在“独特”的情况。
5- 修改您的代码以适应该独特情况。

如果没有其他人提出任何想法,我可能会在家里尝试这个周末。


0

在 Windows 10 上,将“this.MaximizeBox = false”设置为true实际上会破坏Snap。也许这对你有用?


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接