最近一个产品需要用到类似QQ的贴边自动隐藏功能,在网上搜索了不少,CV过去之后效果都不是很理想,而且网上能找到的代码基本上都写在一个方法里面,感觉维护也麻烦,于是研究了下其实现原理(无外乎就是实时监测鼠标位置,在鼠标位置满足条件的时候显示窗体;否则尝试隐藏),自己弄了一个,代码如下:
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Windows;
using System.Windows.Forms;
using System.Windows.Media.Animation;
namespace FAIS6.V3CS.Client.Phone.Core
{
public class HideWindowHelper
{
private readonly Window _window;
private readonly Timer _timer;
private readonly List<HideCore> _hideLogicList = new List<HideCore>();
[DllImport("user32.dll", CharSet = CharSet.Auto)]
private static extern bool GetCursorPos(out Point pt);
private struct Point
{
public int X;
public int Y;
// ReSharper disable once UnusedMember.Local
public Point(int x, int y)
{
this.X = x;
this.Y = y;
}
}
private bool _isHide;
private bool _isStarted;
private HideCore _lastHiderOn;
private bool _isInAnimation;
private HideWindowHelper(Window window)
{
_window = window;
_timer = new Timer {Interval = 300};
_timer.Tick += _timer_Tick;
}
public HideWindowHelper AddHider<THideCore>() where THideCore : HideCore, new()
{
if (_isStarted) throw new Exception("调用了Start方法后无法在添加隐藏逻辑");
var logic = new THideCore();
logic.Init(_window, AnimationReport);
_hideLogicList.Add(logic);
return this;
}
private void _timer_Tick(object sender, EventArgs e)
{
if (_isInAnimation) return;
if (_window.IsActive) return;
GetCursorPos(out var point); //获取鼠标相对桌面的位置
var isMouseEnter = point.X >= _window.Left
&& point.X <= _window.Left + _window.Width + 30
&& point.Y >= _window.Top
&& point.Y <= _window.Top
+ _window.Height + 30;
//鼠标在里面
if (isMouseEnter)
{
//没有隐藏,直接返回
if (!_isHide) return;
//理论上不会出现为null的情况
if (_lastHiderOn != null)
{
_lastHiderOn.Show();
_isHide = false;
_window.ShowInTaskbar = true;
return;
}
}
foreach (var core in _hideLogicList)
{
//鼠标在里面并且没有隐藏
if (isMouseEnter && !_isHide) return;
//鼠标在里面并且当期是隐藏状态且当前处理器成功显示了窗体
if (isMouseEnter && _isHide && core.Show())
{
_isHide = false;
_window.ShowInTaskbar = true;
return;
}
//鼠标在外面并且没有隐藏,那么调用当前处理器尝试隐藏窗体
if (!isMouseEnter && !_isHide && core.Hide())
{
_lastHiderOn = core;
_isHide = true;
_window.ShowInTaskbar = false;
return;
}
}
}
private void AnimationReport(bool isInAnimation)
{
_isInAnimation = isInAnimation;
}
public HideWindowHelper Start()
{
_isStarted = true;
_timer.Start();
return this;
}
public void Stop()
{
_timer.Stop();
_isStarted = false;
}
public static HideWindowHelper CreateFor(Window window)
{
return new HideWindowHelper(window);
}
public void TryShow()
{
if (_lastHiderOn == null) return;
_lastHiderOn.Show();
_isHide = false;
_window.Activate();
}
}
#region 隐藏逻辑基类
public abstract class HideCore
{
private Window _window;
private Action<bool> _animationStateReport;
internal void Init(Window window, Action<bool> animationStateReport)
{
_window = window;
_animationStateReport = animationStateReport;
}
public abstract bool Show();
public abstract bool Hide();
protected Window WindowInstance => _window;
protected void StartAnimation(DependencyProperty property, double from, double to)
{
_animationStateReport(true);
var doubleAnimation = new DoubleAnimation(from, to, TimeSpan.FromSeconds(0.5));
doubleAnimation.Completed += delegate
{
_window.BeginAnimation(property, null);
_animationStateReport(false);
};
_window.BeginAnimation(property, doubleAnimation);
}
}
#endregion
#region 向上隐藏
class HideOnTop : HideCore
{
public override bool Show()
{
if (WindowInstance.Top > 0) return false;
StartAnimation(Window.TopProperty, WindowInstance.Top, 0);
return true;
}
public override bool Hide()
{
if (WindowInstance.Top > 2) return false;
StartAnimation(Window.TopProperty, WindowInstance.Top, 0 - WindowInstance.Top - WindowInstance.Height + 2);
return true;
}
}
#endregion
#region 向左隐藏
class HideOnLeft : HideCore
{
public override bool Show()
{
if (WindowInstance.Left > 0) return false;
StartAnimation(Window.LeftProperty, WindowInstance.Left, 0);
return true;
}
public override bool Hide()
{
if (WindowInstance.Left > 2) return false;
StartAnimation(Window.LeftProperty, WindowInstance.Left, 0 - WindowInstance.Width + 2);
return true;
}
}
#endregion
#region 向右隐藏
class HideOnRight : HideCore
{
private readonly int _screenWidth;
public HideOnRight()
{
foreach (var screen in Screen.AllScreens)
{
_screenWidth += screen.Bounds.Width;
}
}
public override bool Show()
{
if (_screenWidth - WindowInstance.Left - WindowInstance.Width > 0) return false;
StartAnimation(Window.LeftProperty, WindowInstance.Left, _screenWidth - WindowInstance.Width);
return true;
}
public override bool Hide()
{
if (_screenWidth - WindowInstance.Left - WindowInstance.Width > 2) return false;
StartAnimation(Window.LeftProperty, WindowInstance.Left, _screenWidth - 2);
return true;
}
}
#endregion
}
代码将公共逻辑抽离,然后由单独的子类实现具体的隐藏和显示逻辑,外部调用代码如下:
_hideWindowHelper = HideWindowHelper
.CreateFor(this)
.AddHider<HideOnLeft>()
.AddHider<HideOnRight>()
.AddHider<HideOnTop>();
初始化隐藏助手,并且添加需要实现的贴边功能,左右优先,然后是向上隐藏
_hideWindowHelper.Start(); //启动隐藏
_hideWindowHelper.TryShow();//用代码触发显示,适用于双击任务栏图标
用户也可以自行实现贴边功能,只需要继承HideCore类即可