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 { /// /// 高性能透明桌面窗口 /// public partial class PerformanceTransparentWin : Window { static class BrushCreator { /// /// 尝试从缓存获取或创建颜色笔刷 /// /// 对应的字符串颜色 /// 已经被 Freeze 的颜色笔刷 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)); } return brush; } private static Dictionary> BrushCacheList { get; } = new Dictionary>(); 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; } } /// /// 创建高性能透明桌面窗口 /// 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(); } } /// /// 设置点击穿透到后面透明的窗口 /// 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; } } /// /// 设置点击命中,不会穿透到后面的窗口 /// 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; }); } /// /// 是否开启 DWM 了,如果开启了,那么才可以使用高性能的桌面透明窗口 /// private readonly bool _dwmEnabled; private readonly IntPtr _hwnd; } }