WPF实现边缘依靠效果

发布时间 2023-12-15 11:10:51作者: zhaotianff

最近做的某个功能需要用到边缘依靠,WPF实现了下,效果如下

 

 

主要实现原理如下:

1、增加一块热点区域,鼠标进入时,触发显示动画,并隐藏热点区域

2、鼠标拖动或离开窗体,判断窗体离屏幕边缘的距离,符合条件的,触发隐藏动画,并显示热点区域

3、使用Window.Left属性进行窗体动画

 

需要注意的地方:

1、在拖动窗体时,不能通过窗体的Left属性来进行判断,因为Left没有刷新,可以通过Win32 API 函数GetWindowRect来获取

2、可以增加缓动动画,使动画效果更好。

 

实现代码如下:

 

MainWindow.xaml

在XAML里定义了显示和隐藏的动画,窗体定义了两列,第一列就是隐藏时用于显示窗体的热点区域

 1 <blur:BlurWindow x:Class="WpfDockDemo.MainWindow"
 2         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
 3         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
 4         xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
 5         xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
 6         xmlns:local="clr-namespace:WpfDockDemo"
 7         xmlns:blur="clr-namespace:TianXiaTech;assembly=BlurWindow"
 8         mc:Ignorable="d"
 9         Title="MainWindow" Height="650" Width="305" TitleVisibility="Collapsed" IconVisibility="Collapsed" ControlBoxVisibility="Collapsed" 
10                  Background="White" Name="main" MouseMove="BlurWindow_MouseMove" MouseLeave="main_MouseLeave" Topmost="True" PreviewMouseDown="main_MouseDown">
11     <blur:BlurWindow.Resources>
12         <Storyboard x:Key="hiddenAnimation">
13             <DoubleAnimation Storyboard.TargetName="main" Storyboard.TargetProperty="(Window.Left)" Duration="0:0:0.5" AutoReverse="False">
14                 <DoubleAnimation.EasingFunction>
15                     <BackEase Amplitude="0.3"/>
16                 </DoubleAnimation.EasingFunction>
17             </DoubleAnimation>
18         </Storyboard>
19 
20         <Storyboard x:Key="showAnimation">
21             <DoubleAnimation Storyboard.TargetName="main" Storyboard.TargetProperty="(Window.Left)" Duration="0:0:0.5" AutoReverse="False">
22                 <DoubleAnimation.EasingFunction>
23                     <BackEase Amplitude="0.3" />
24                 </DoubleAnimation.EasingFunction>
25             </DoubleAnimation>
26         </Storyboard>
27     </blur:BlurWindow.Resources>
28     <Grid>
29 
30         <Grid.ColumnDefinitions>
31             <ColumnDefinition Width="5"/>
32             <ColumnDefinition/>
33         </Grid.ColumnDefinitions>
34 
35         <Grid Background="Transparent" MouseEnter="grid_DockArea_MouseEnter" Name="grid_DockArea" Visibility="Collapsed">
36             <Border Height="{Binding ElementName=main,Path=ActualHeight}" VerticalAlignment="Center" Width="2" Background="Silver">
37                 <Border.Effect>
38                     <DropShadowEffect Color="Black" Opacity=".3"></DropShadowEffect>
39                 </Border.Effect>
40             </Border>
41         </Grid>
42 
43         <!--网上随便找的图-->
44         <Image Margin="10" Source="https://pic1.zhimg.com/80/v2-46ae37aad7cb70b4dc4ad4489cdaffdd_720w.webp?source=2c26e567" Grid.Column="1" PreviewMouseDown="main_MouseDown" >
45             <Image.Effect>
46                 <DropShadowEffect Opacity=".5"></DropShadowEffect>
47             </Image.Effect>
48         </Image>
49     </Grid>
50 </blur:BlurWindow>

 

引入用到的api函数

 1   public struct POINT
 2     {
 3         public int x;
 4         public int y;
 5     }
 6 
 7     public struct RECT
 8     {
 9         public int left;
10         public int top;
11         public int right;
12         public int bottom;
13     }
14 
15     public class User32
16     {
17         [DllImport("User32.dll")]
18         public static extern int GetCursorPos(ref POINT point);
19 
20         [DllImport("User32.dll")]
21         public static extern bool GetWindowRect(IntPtr hWnd, ref RECT lpRect);
22     }

 

定义一些用于记录窗体状态的变量

1  private bool isDocking = true;  //是否启用边缘停靠
2  private bool isAnimation = false; //是否正在动画中
3  private bool isDraged = false;     //是否正在拖动中

 

定义用于窗体显示和隐藏动画的函数

 1   private void HideWindow(double left = -1)
 2         {
 3             if (left == -1)
 4             {
 5                 RECT rect = new RECT();
 6                 User32.GetWindowRect(new WindowInteropHelper(this).Handle, ref rect);
 7                 left = rect.left;
 8             }
 9 
10             if (SystemParameters.PrimaryScreenWidth - left - this.Width > 15)
11                 return;
12 
13             if (isAnimation)
14                 return;
15 
16             isAnimation = true;
17             hiddenAnimation.Begin();
18         }
19 
20         private void ShowWindow()
21         {
22             if (isAnimation)
23                 return;
24 
25             grid_DockArea.Visibility = Visibility.Collapsed;
26             isAnimation = true;
27             showAnimation.Begin();
28         }

 

处理鼠标移动事件

 1 private void BlurWindow_MouseMove(object sender, MouseEventArgs e)
 2         {
 3             if (e.LeftButton == MouseButtonState.Pressed)
 4             {
 5                 try
 6                 {
 7                     this.DragMove();
 8                     isDraged = true;
 9                 }
10                 catch
11                 {
12 
13                 }
14             }
15 
16             if (e.LeftButton == MouseButtonState.Released && isDocking == true && isDraged == true)
17             {
18                 POINT point = new POINT();
19                 if (User32.GetCursorPos(ref point) == 1)
20                 {
21                     var pos = e.GetPosition(this);
22 
23                     if (pos.X < 0 && pos.Y < 0)
24                         HideWindow();
25                 }
26 
27                 isDraged = false;
28             }
29         }

 

处理鼠标按下事件

1         private void main_MouseDown(object sender, MouseButtonEventArgs e)
2         {
3             var pos = e.GetPosition(this);
4             if (pos.X >= 0 && pos.Y >= 0)
5                 isDraged = true;
6         }

 

处理鼠标离开事件

1         private void main_MouseLeave(object sender, MouseEventArgs e)
2         {
3             if (isDocking && isDraged == false)
4             {
5                 HideWindow();
6             }
7         }

 

这样就可以实现了一个简单的边缘停靠效果。

 

窗体第一次运行是不会主动隐藏的,需要手动控制  一下。

 

示例代码