-
Notifications
You must be signed in to change notification settings - Fork 183
Expand file tree
/
Copy pathMainFormWinHelper.cs
More file actions
195 lines (173 loc) · 8.15 KB
/
MainFormWinHelper.cs
File metadata and controls
195 lines (173 loc) · 8.15 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
using System;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Runtime.InteropServices;
using System.Threading.Tasks;
using System.Windows.Forms;
using LiteMonitor.src.Core;
namespace LiteMonitor.src.UI.Helpers
{
/// <summary>
/// 主窗口底层助手 (Windows Helper)
/// 职责:封装 Win32 API、窗口样式、圆角、穿透、透明度等底层操作
/// </summary>
public class MainFormWinHelper
{
private readonly Form _form;
public MainFormWinHelper(Form form)
{
_form = form;
}
public void InitializeStyle(bool topMost, bool clickThrough)
{
_form.FormBorderStyle = FormBorderStyle.None;
_form.ShowInTaskbar = false;
_form.TopMost = topMost;
// 解决 DoubleBuffered 访问权限问题
typeof(Control).GetProperty("DoubleBuffered", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance)
?.SetValue(_form, true, null);
// 原始逻辑还原:
// 在原始代码中,Opacity 是在构造函数末尾启动异步任务来渐变的
// 而不是在 InitializeStyle 这种早期阶段直接设为 0
// 这里的 Opacity = 0 会导致窗体在 OnShown 之前就是透明的,
// 但如果 SetStyle(ControlStyles.SupportsTransparentBackColor, true) 还没生效或冲突
// 可能会导致这一瞬间的绘制异常(如黑色闪烁)
//
// 修正:删除这里的 _form.Opacity = 0;
// 改回完全依赖 StartFadeIn 方法来控制,并且那个方法是在 OnShown 之后调用的
ApplyRoundedCorners();
if (clickThrough) SetClickThrough(true);
// 绑定 Resize 事件以自动重绘圆角
_form.Resize += (_, __) => ApplyRoundedCorners();
}
// =================================================================
// 透明度渐变
// =================================================================
public void StartFadeIn(double targetOpacity)
{
targetOpacity = Math.Clamp(targetOpacity, 0.1, 1.0);
_ = Task.Run(async () =>
{
try
{
double current = 0;
while (current < targetOpacity)
{
await Task.Delay(16).ConfigureAwait(false);
_form.BeginInvoke(new Action(() =>
{
current += 0.05;
if (current > targetOpacity) current = targetOpacity;
_form.Opacity = current;
}));
if (current >= targetOpacity) break;
}
}
catch { }
});
}
// =================================================================
// 鼠标穿透
// =================================================================
public void SetClickThrough(bool enable)
{
try
{
int ex = GetWindowLong(_form.Handle, GWL_EXSTYLE);
if (enable)
SetWindowLong(_form.Handle, GWL_EXSTYLE, ex | WS_EX_TRANSPARENT | WS_EX_LAYERED);
else
SetWindowLong(_form.Handle, GWL_EXSTYLE, ex & ~WS_EX_TRANSPARENT);
}
catch { }
}
// =================================================================
// 圆角处理 (Hybrid: Win11 DWM / Win10 Region)
// =================================================================
public void ApplyRoundedCorners()
{
try
{
bool isWin11 = Environment.OSVersion.Version.Major >= 10 && Environment.OSVersion.Version.Build >= 22000;
if (isWin11)
{
_form.Region = null;
int preference = DWMWCP_ROUND;
DwmSetWindowAttribute(_form.Handle, DWMWA_WINDOW_CORNER_PREFERENCE, ref preference, sizeof(int));
int borderColor = DWMWA_COLOR_NONE;
DwmSetWindowAttribute(_form.Handle, DWMWA_BORDER_COLOR, ref borderColor, sizeof(int));
}
else
{
var t = ThemeManager.Current;
int r = Math.Max(0, t.Layout.CornerRadius);
if (r == 0)
{
_form.Region = null;
return;
}
using var gp = new GraphicsPath();
int d = r * 2;
gp.AddArc(0, 0, d, d, 180, 90);
gp.AddArc(_form.Width - d, 0, d, d, 270, 90);
gp.AddArc(_form.Width - d, _form.Height - d, d, d, 0, 90);
gp.AddArc(0, _form.Height - d, d, d, 90, 90);
gp.CloseFigure();
_form.Region?.Dispose();
_form.Region = new Region(gp);
}
}
catch { }
}
// =================================================================
// 置顶守护
// =================================================================
/// <summary>
/// 通过 Win32 API 无条件强制置顶窗口。
/// 不检查 WS_EX_TOPMOST 标志,因为在全屏应用(如 VMware、无边框全屏游戏)中
/// 该标志可能仍然存在但窗口实际被覆盖,必须重新调用 SetWindowPos 才能恢复显示。
/// </summary>
public void ReapplyTopMost()
{
try
{
SetWindowPos(_form.Handle, HWND_TOPMOST, 0, 0, 0, 0,
SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
}
catch { }
}
// =================================================================
// Win32 API
// =================================================================
public static int RegisterTaskbarCreatedMessage()
{
int msgId = RegisterWindowMessage("TaskbarCreated");
if (msgId != 0)
{
ChangeWindowMessageFilter((uint)msgId, MSGFLT_ADD);
}
return msgId;
}
[DllImport("dwmapi.dll")] private static extern int DwmSetWindowAttribute(IntPtr hwnd, int attr, ref int attrValue, int attrSize);
[DllImport("user32.dll", SetLastError = true)] private static extern int GetWindowLong(IntPtr hWnd, int nIndex);
[DllImport("user32.dll", SetLastError = true)] private static extern int SetWindowLong(IntPtr hWnd, int nIndex, int dwNewLong);
[DllImport("user32.dll", SetLastError = true)] private static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int X, int Y, int cx, int cy, uint uFlags);
[DllImport("user32.dll")] [return: MarshalAs(UnmanagedType.Bool)] public static extern bool SetForegroundWindow(IntPtr hWnd);
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)] public static extern int RegisterWindowMessage(string lpString);
[DllImport("user32.dll", SetLastError = true)] public static extern bool ChangeWindowMessageFilter(uint message, uint dwFlag);
public const uint MSGFLT_ADD = 1;
private static readonly IntPtr HWND_TOPMOST = new IntPtr(-1);
private const uint SWP_NOMOVE = 0x0002;
private const uint SWP_NOSIZE = 0x0001;
private const uint SWP_NOACTIVATE = 0x0010;
private const int DWMWA_WINDOW_CORNER_PREFERENCE = 33;
private const int DWMWCP_ROUND = 2;
private const int DWMWA_BORDER_COLOR = 34;
private const int DWMWA_COLOR_NONE = unchecked((int)0xFFFFFFFE);
private const int GWL_EXSTYLE = -20;
private const int WS_EX_TRANSPARENT = 0x20;
private const int WS_EX_LAYERED = 0x80000;
private const int WS_EX_TOPMOST = 0x00000008;
public static void ActivateWindow(IntPtr handle) => SetForegroundWindow(handle);
}
}