212 lines
8.0 KiB
C#
212 lines
8.0 KiB
C#
|
|
using System;
|
|||
|
|
using System.Collections.Generic;
|
|||
|
|
using System.Linq;
|
|||
|
|
using System.Runtime.InteropServices;
|
|||
|
|
using System.Text;
|
|||
|
|
using System.Threading.Tasks;
|
|||
|
|
using static System.Windows.Forms.VisualStyles.VisualStyleElement.TrayNotify;
|
|||
|
|
using System.Windows.Controls;
|
|||
|
|
using System.Windows.Interop;
|
|||
|
|
using System.Windows.Shell;
|
|||
|
|
using System.Windows;
|
|||
|
|
using System.Windows.Input;
|
|||
|
|
using System.Windows.Media;
|
|||
|
|
|
|||
|
|
namespace Ink_Canvas.Helpers
|
|||
|
|
{
|
|||
|
|
/// <summary>
|
|||
|
|
/// 高性能透明桌面窗口
|
|||
|
|
/// </summary>
|
|||
|
|
public partial class PerformanceTransparentWin : Window
|
|||
|
|
{
|
|||
|
|
|
|||
|
|
static class BrushCreator
|
|||
|
|
{
|
|||
|
|
/// <summary>
|
|||
|
|
/// 尝试从缓存获取或创建颜色笔刷
|
|||
|
|
/// </summary>
|
|||
|
|
/// <param name="color">对应的字符串颜色</param>
|
|||
|
|
/// <returns>已经被 Freeze 的颜色笔刷</returns>
|
|||
|
|
public static SolidColorBrush GetOrCreate(string color)
|
|||
|
|
{
|
|||
|
|
if (!color.StartsWith("#"))
|
|||
|
|
{
|
|||
|
|
throw new ArgumentException($"输入的{nameof(color)}不是有效颜色,需要使用 # 开始");
|
|||
|
|
// 如果不使用 # 开始将会在 ConvertFromString 出现异常
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (TryGetBrush(color, out var brushValue))
|
|||
|
|
{
|
|||
|
|
return (SolidColorBrush)brushValue;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
object convertColor;
|
|||
|
|
|
|||
|
|
try
|
|||
|
|
{
|
|||
|
|
convertColor = ColorConverter.ConvertFromString(color);
|
|||
|
|
}
|
|||
|
|
catch (FormatException)
|
|||
|
|
{
|
|||
|
|
// 因为在 ConvertFromString 会抛出的是 令牌无效 难以知道是为什么传入的不对
|
|||
|
|
throw new ArgumentException($"输入的{nameof(color)}不是有效颜色");
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (convertColor == null)
|
|||
|
|
{
|
|||
|
|
throw new ArgumentException($"输入的{nameof(color)}不是有效颜色");
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
var brush = new SolidColorBrush((Color)convertColor);
|
|||
|
|
if (TryFreeze(brush))
|
|||
|
|
{
|
|||
|
|
BrushCacheList.Add(color, new WeakReference<Brush>(brush));
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return brush;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private static Dictionary<string, WeakReference<Brush>> BrushCacheList { get; } =
|
|||
|
|
new Dictionary<string, WeakReference<Brush>>();
|
|||
|
|
|
|||
|
|
private static bool TryGetBrush(string key, out Brush brush)
|
|||
|
|
{
|
|||
|
|
if (BrushCacheList.TryGetValue(key, out var brushValue))
|
|||
|
|
{
|
|||
|
|
if (brushValue.TryGetTarget(out brush))
|
|||
|
|
{
|
|||
|
|
return true;
|
|||
|
|
}
|
|||
|
|
else
|
|||
|
|
{
|
|||
|
|
// 被回收的资源
|
|||
|
|
BrushCacheList.Remove(key);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
brush = null;
|
|||
|
|
return false;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private static bool TryFreeze(Freezable freezable)
|
|||
|
|
{
|
|||
|
|
if (freezable.CanFreeze)
|
|||
|
|
{
|
|||
|
|
freezable.Freeze();
|
|||
|
|
return true;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return false;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 创建高性能透明桌面窗口
|
|||
|
|
/// </summary>
|
|||
|
|
public PerformanceTransparentWin()
|
|||
|
|
{
|
|||
|
|
WindowStyle = WindowStyle.None;
|
|||
|
|
ResizeMode = ResizeMode.NoResize;
|
|||
|
|
|
|||
|
|
Stylus.SetIsFlicksEnabled(this, false);
|
|||
|
|
Stylus.SetIsPressAndHoldEnabled(this, false);
|
|||
|
|
Stylus.SetIsTapFeedbackEnabled(this, false);
|
|||
|
|
Stylus.SetIsTouchFeedbackEnabled(this, false);
|
|||
|
|
|
|||
|
|
WindowChrome.SetWindowChrome(this,
|
|||
|
|
new WindowChrome { GlassFrameThickness = WindowChrome.GlassFrameCompleteThickness, CaptionHeight = 0, CornerRadius = new CornerRadius(0), ResizeBorderThickness = new Thickness(0)});
|
|||
|
|
|
|||
|
|
var visualTree = new FrameworkElementFactory(typeof(Border));
|
|||
|
|
visualTree.SetValue(Border.BackgroundProperty, new TemplateBindingExtension(Window.BackgroundProperty));
|
|||
|
|
var childVisualTree = new FrameworkElementFactory(typeof(ContentPresenter));
|
|||
|
|
childVisualTree.SetValue(UIElement.ClipToBoundsProperty, true);
|
|||
|
|
visualTree.AppendChild(childVisualTree);
|
|||
|
|
|
|||
|
|
Template = new ControlTemplate
|
|||
|
|
{
|
|||
|
|
TargetType = typeof(Window),
|
|||
|
|
VisualTree = visualTree,
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
_dwmEnabled = DwmCompositionHelper.DwmIsCompositionEnabled();
|
|||
|
|
if (_dwmEnabled)
|
|||
|
|
{
|
|||
|
|
_hwnd = new WindowInteropHelper(this).EnsureHandle();
|
|||
|
|
Loaded += PerformanceDesktopTransparentWindow_Loaded;
|
|||
|
|
Background = Brushes.Transparent;
|
|||
|
|
}
|
|||
|
|
else
|
|||
|
|
{
|
|||
|
|
AllowsTransparency = true;
|
|||
|
|
Background = BrushCreator.GetOrCreate("#0100000");
|
|||
|
|
_hwnd = new WindowInteropHelper(this).EnsureHandle();
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 设置点击穿透到后面透明的窗口
|
|||
|
|
/// </summary>
|
|||
|
|
public void SetTransparentHitThrough()
|
|||
|
|
{
|
|||
|
|
if (_dwmEnabled)
|
|||
|
|
{
|
|||
|
|
Win32.User32.SetWindowLongPtr(_hwnd, Win32.GetWindowLongFields.GWL_EXSTYLE,
|
|||
|
|
(IntPtr)(int)((long)Win32.User32.GetWindowLongPtr(_hwnd, Win32.GetWindowLongFields.GWL_EXSTYLE) | (long)Win32.ExtendedWindowStyles.WS_EX_TRANSPARENT));
|
|||
|
|
}
|
|||
|
|
else
|
|||
|
|
{
|
|||
|
|
Background = Brushes.Transparent;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 设置点击命中,不会穿透到后面的窗口
|
|||
|
|
/// </summary>
|
|||
|
|
public void SetTransparentNotHitThrough()
|
|||
|
|
{
|
|||
|
|
if (_dwmEnabled)
|
|||
|
|
{
|
|||
|
|
Win32.User32.SetWindowLongPtr(_hwnd, Win32.GetWindowLongFields.GWL_EXSTYLE,
|
|||
|
|
(IntPtr)(int)((long)Win32.User32.GetWindowLongPtr(_hwnd, Win32.GetWindowLongFields.GWL_EXSTYLE) & ~(long)Win32.ExtendedWindowStyles.WS_EX_TRANSPARENT));
|
|||
|
|
}
|
|||
|
|
else
|
|||
|
|
{
|
|||
|
|
Background = BrushCreator.GetOrCreate("#0100000");
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
[StructLayout(LayoutKind.Sequential)]
|
|||
|
|
private struct STYLESTRUCT
|
|||
|
|
{
|
|||
|
|
public int styleOld;
|
|||
|
|
public int styleNew;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private void PerformanceDesktopTransparentWindow_Loaded(object sender, RoutedEventArgs e)
|
|||
|
|
{
|
|||
|
|
((HwndSource)PresentationSource.FromVisual(this)).AddHook((IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled) =>
|
|||
|
|
{
|
|||
|
|
//想要让窗口透明穿透鼠标和触摸等,需要同时设置 WS_EX_LAYERED 和 WS_EX_TRANSPARENT 样式,
|
|||
|
|
//确保窗口始终有 WS_EX_LAYERED 这个样式,并在开启穿透时设置 WS_EX_TRANSPARENT 样式
|
|||
|
|
//但是WPF窗口在未设置 AllowsTransparency = true 时,会自动去掉 WS_EX_LAYERED 样式(在 HwndTarget 类中),
|
|||
|
|
//如果设置了 AllowsTransparency = true 将使用WPF内置的低性能的透明实现,
|
|||
|
|
//所以这里通过 Hook 的方式,在不使用WPF内置的透明实现的情况下,强行保证这个样式存在。
|
|||
|
|
if (msg == (int)Win32.WM.STYLECHANGING && (long)wParam == (long)Win32.GetWindowLongFields.GWL_EXSTYLE)
|
|||
|
|
{
|
|||
|
|
var styleStruct = (STYLESTRUCT)Marshal.PtrToStructure(lParam, typeof(STYLESTRUCT));
|
|||
|
|
styleStruct.styleNew |= (int)Win32.ExtendedWindowStyles.WS_EX_LAYERED;
|
|||
|
|
Marshal.StructureToPtr(styleStruct, lParam, false);
|
|||
|
|
handled = true;
|
|||
|
|
}
|
|||
|
|
return IntPtr.Zero;
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 是否开启 DWM 了,如果开启了,那么才可以使用高性能的桌面透明窗口
|
|||
|
|
/// </summary>
|
|||
|
|
private readonly bool _dwmEnabled;
|
|||
|
|
private readonly IntPtr _hwnd;
|
|||
|
|
}
|
|||
|
|
}
|