WPF实现边缘停靠效果
最近做的某个功能需要用到边缘停靠,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 }
这样就可以实现了一个简单的边缘停靠效果。现在只实现了屏幕右侧的边缘停靠,左边和上面可以如法炮制。
窗体第一次运行是不会主动隐藏的,需要手动控制 一下。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?