How do you disable Aero Snap in an application?

前端 未结 11 2271
一向
一向 2020-11-29 09:36

Is it possible to disable the automatic window-docking feature of Windows 7 in a WPF application?

相关标签:
11条回答
  • 2020-11-29 10:04

    Here's my solution. Windows will not snap if their ResizeMode is set to ResizeMode.NoResize, therefore, the trick is to determine reliably when a drag/move begins and ends.

    EDIT: alexandrud correctly pointed out that this will only work for "borderless" windows (WindowStyle = None in WPF terms).

    Many Bothans died to bring us this information.

    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;
        }
    }
    
    0 讨论(0)
  • 2020-11-29 10:05

    If you are giving example of "Sticky Notes" of Win7, you may have noticed that it does NOT have standard window border. On that as a base, I can only tell you that there's no direct way of doing this except you set ResizeMode="NoResize" and handling the resize behavior manually. Following is a very basic, non-professional solution that i've quickly created to get you started, but you can append more functions if you like :)

    <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>
    

    You can even make a control (basically a panel) that can be resized and moved within its parent canvas. Now this control can be filled into a transparent maximized window. This'll give you an illusion of your control being a window that doesn't respond to 'Window Snap' and will not dock!

    Hope this helps.
    Regards,
    Mihir Gokani

    0 讨论(0)
  • 2020-11-29 10:09

    A rather simple solution I found that works with borderless windows as well: Just hide the maximize button (event if it's already not displayed due to the lack of caption bar):

        [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 讨论(0)
  • 2020-11-29 10:15

    I recently needed to do this to a custom, resizable ResizeMode = CanResizeWithGrip WPF window with no window decorations (no title bar and buttons). I used DragMove() to move the window, and when It is maximized by AeroSnap, the window becomes unmovable and hence locked in place.

    I tried Barn Monkey's solution, which partially worked, but it would still show the AeroSnap graphic and resize the app to fullscreen size. I modified it below and now it works as expect: still resizable, but no AeroSnap at all.

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

    EDIT:

    It's been a while since I wrote this, but since people still look at this I'll update it with what I use now. I still use basically the same method for preventing edge snapping and moving my windows, but I now have them packed into custom Behavior<> classes that I can attach to a Window or UserControl. This makes them very easy to use with MVVM (I use Caliburn Micro).

    The behavior classes are:

    /// <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> { }
    

    I then attach them to my dialog box Views with:

    <UserControl ...
      xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
      xmlns:util:="...">
      <i:Interaction.Behaviors>
        <util:UCNoSnapBehavior/>
        <util:UCDragMoveBehavior/>
      </i:Interaction.Behaviors>
    
      ...
    
    </UserControl>
    

    And it just works!

    0 讨论(0)
  • 2020-11-29 10:20

    It might not be the perfect solution for you, but for me setting the form as non-resizable did the trick.

    0 讨论(0)
  • 2020-11-29 10:20

    I don't have a Windows 7 box here, so I can't test this, but here's what I would try:

    1- Create a test form and override the WndProc
    2- Test and log specific messages pertaining to Size, Position and WindowState changing.
    3- Determine if the messages sent to the window when docked are a combination of Size/Position/WindowState or if there is another, new Windows 7 message (5 minutes of searching didn't reveal anything to me.)
    4- Once you have the messages, check to see if there is a "unique" case that is occurring.
    5- Modify your code to accommodate that unique case.

    If no one else comes up with anything, I might give this a whirl at home this weekend.

    0 讨论(0)
提交回复
热议问题