mirror of
https://github.com/modernw/App-Installer-For-Windows-8.x-Reset.git
synced 2026-04-11 17:57:19 +10:00
1001 lines
28 KiB
C#
1001 lines
28 KiB
C#
|
||
using System;
|
||
using System.ComponentModel;
|
||
using System.Drawing;
|
||
using System.Runtime.InteropServices;
|
||
using System.Windows.Forms;
|
||
using DataUtils;
|
||
|
||
namespace WAShell
|
||
{
|
||
public partial class ModernForm: Form, IMetroIconSupport
|
||
{
|
||
// ==================== P/Invoke ====================
|
||
private const int WM_NCCALCSIZE = 0x0083;
|
||
private const int WM_NCHITTEST = 0x0084;
|
||
private const int WM_NCPAINT = 0x0085;
|
||
private const int WM_NCACTIVATE = 0x0086;
|
||
private const int WM_MOUSEMOVE = 0x0200;
|
||
private const int WM_LBUTTONDOWN = 0x0201;
|
||
private const int WM_LBUTTONUP = 0x0202;
|
||
private const int WM_NCLBUTTONDOWN = 0x00A1;
|
||
private const int WM_NCLBUTTONUP = 0x00A2;
|
||
private const int WM_PAINT = 0x000F;
|
||
private const int WM_SETTEXT = 0x000C;
|
||
private const int WM_SIZE = 0x0005;
|
||
private const int WM_MOUSELEAVE = 0x02A3;
|
||
private const int WM_GETMINMAXINFO = 0x0024;
|
||
private const int WM_DISPLAYCHANGE = 0x007E;
|
||
|
||
private const int HTCLIENT = 1;
|
||
private const int HTCAPTION = 2;
|
||
private const int HTCLOSE = 20;
|
||
private const int HTMAXBUTTON = 9;
|
||
private const int HTMINBUTTON = 8;
|
||
private const int HTLEFT = 10;
|
||
private const int HTRIGHT = 11;
|
||
private const int HTTOP = 12;
|
||
private const int HTTOPLEFT = 13;
|
||
private const int HTTOPRIGHT = 14;
|
||
private const int HTBOTTOM = 15;
|
||
private const int HTBOTTOMLEFT = 16;
|
||
private const int HTBOTTOMRIGHT = 17;
|
||
|
||
private const int CS_DROPSHADOW = 0x00020000;
|
||
|
||
[StructLayout (LayoutKind.Sequential)]
|
||
private struct MINMAXINFO
|
||
{
|
||
public Point ptReserved;
|
||
public Point ptMaxSize;
|
||
public Point ptMaxPosition;
|
||
public Point ptMinTrackSize;
|
||
public Point ptMaxTrackSize;
|
||
}
|
||
|
||
[StructLayout (LayoutKind.Sequential)]
|
||
private struct NCCALCSIZE_PARAMS
|
||
{
|
||
[MarshalAs (UnmanagedType.ByValArray, SizeConst = 3)]
|
||
public RECT [] rgrc;
|
||
public IntPtr lppos;
|
||
}
|
||
|
||
[StructLayout (LayoutKind.Sequential)]
|
||
private struct RECT
|
||
{
|
||
public int left;
|
||
public int top;
|
||
public int right;
|
||
public int bottom;
|
||
}
|
||
|
||
[StructLayout (LayoutKind.Sequential)]
|
||
private struct MARGINS
|
||
{
|
||
public int cxLeftWidth;
|
||
public int cxRightWidth;
|
||
public int cyTopHeight;
|
||
public int cyBottomHeight;
|
||
}
|
||
|
||
[DllImport ("user32.dll", SetLastError = true)]
|
||
private static extern IntPtr GetWindowDC (IntPtr hWnd);
|
||
|
||
[DllImport ("user32.dll")]
|
||
private static extern int ReleaseDC (IntPtr hWnd, IntPtr hDC);
|
||
|
||
[DllImport ("user32.dll")]
|
||
private static extern bool GetWindowRect (IntPtr hWnd, out RECT lpRect);
|
||
|
||
[DllImport ("user32.dll")]
|
||
private static extern bool GetClientRect (IntPtr hWnd, out RECT lpRect);
|
||
|
||
[DllImport ("user32.dll")]
|
||
private static extern bool ReleaseCapture ();
|
||
|
||
[DllImport ("user32.dll")]
|
||
private static extern int SendMessage (IntPtr hWnd, int Msg, IntPtr wParam, IntPtr lParam);
|
||
|
||
[DllImport ("user32.dll")]
|
||
private static extern bool TrackMouseEvent (ref TRACKMOUSEEVENT tme);
|
||
|
||
[DllImport ("user32.dll")]
|
||
private static extern IntPtr MonitorFromWindow (IntPtr hwnd, int dwFlags);
|
||
|
||
[DllImport ("user32.dll")]
|
||
private static extern bool GetMonitorInfo (IntPtr hMonitor, ref MONITORINFO lpmi);
|
||
|
||
[DllImport ("dwmapi.dll", PreserveSig = false)]
|
||
private static extern void DwmExtendFrameIntoClientArea (IntPtr hWnd, ref MARGINS pMarInset);
|
||
|
||
[DllImport ("dwmapi.dll")]
|
||
private static extern int DwmIsCompositionEnabled (out bool pfEnabled);
|
||
|
||
[StructLayout (LayoutKind.Sequential)]
|
||
private struct MONITORINFO
|
||
{
|
||
public int cbSize;
|
||
public RECT rcMonitor;
|
||
public RECT rcWork;
|
||
public int dwFlags;
|
||
}
|
||
|
||
[StructLayout (LayoutKind.Sequential)]
|
||
private struct TRACKMOUSEEVENT
|
||
{
|
||
public int cbSize;
|
||
public int dwFlags;
|
||
public IntPtr hwndTrack;
|
||
public int dwHoverTime;
|
||
}
|
||
|
||
private const int TME_LEAVE = 0x00000002;
|
||
private const int MONITOR_DEFAULTTONULL = 0;
|
||
|
||
private const int WM_SYSCOMMAND = 0x0112;
|
||
private const int SC_MOVE = 0xF010;
|
||
private const int SC_SIZE = 0xF000;
|
||
|
||
// ==================== 字段和属性 ====================
|
||
private int _titleBarHeight;
|
||
private int _borderWidth;
|
||
private int _resizeAreaWidth = 10;
|
||
|
||
// DPI 适配的尺寸
|
||
private int _iconSize;
|
||
private int _iconOffsetLeft;
|
||
private int _iconOffsetTop;
|
||
private int _buttonDisplaySize;
|
||
|
||
private Color _titleBarColorActive = Color.FromArgb (17, 17, 17);
|
||
private bool _isFormActive = true;
|
||
|
||
private Rectangle _minButtonRect;
|
||
private Rectangle _maxButtonRect;
|
||
private Rectangle _closeButtonRect;
|
||
private Rectangle _iconRect;
|
||
|
||
private bool _isMouseOverMinButton = false;
|
||
private bool _isMouseOverMaxButton = false;
|
||
private bool _isMouseOverCloseButton = false;
|
||
|
||
// 按钮按压状态追踪
|
||
private bool _isMinButtonPressed = false;
|
||
private bool _isMaxButtonPressed = false;
|
||
private bool _isCloseButtonPressed = false;
|
||
|
||
// 资源管理器
|
||
private System.ComponentModel.ComponentResourceManager _res;
|
||
|
||
// 按钮资源缓存(原始尺寸)
|
||
private Bitmap _minNormal, _minLight, _minPress;
|
||
private Bitmap _maxNormal, _maxLight, _maxPress;
|
||
private Bitmap _restoreNormal, _restoreLight, _restorePress;
|
||
private Bitmap _cancelNormal, _cancelLight, _cancelPress;
|
||
|
||
// 缩放后的按钮资源缓存
|
||
private Bitmap _minNormalScaled, _minLightScaled, _minPressScaled;
|
||
private Bitmap _maxNormalScaled, _maxLightScaled, _maxPressScaled;
|
||
private Bitmap _restoreNormalScaled, _restoreLightScaled, _restorePressScaled;
|
||
private Bitmap _cancelNormalScaled, _cancelLightScaled, _cancelPressScaled;
|
||
|
||
// 窗口图标
|
||
private Icon _windowIcon;
|
||
|
||
private enum ButtonState
|
||
{
|
||
Normal,
|
||
Hover,
|
||
Pressed
|
||
}
|
||
|
||
private ButtonState _minState = ButtonState.Normal;
|
||
private ButtonState _maxState = ButtonState.Normal;
|
||
private ButtonState _closeState = ButtonState.Normal;
|
||
|
||
private bool _trackingMouse = false;
|
||
private bool _buttonRectsCalculated = false;
|
||
|
||
// 防止重复的 NCPAINT 请求
|
||
private bool _isInNCPaint = false;
|
||
|
||
public ModernForm ()
|
||
{
|
||
InitializeComponent ();
|
||
SetStyle (ControlStyles.AllPaintingInWmPaint | ControlStyles.UserPaint | ControlStyles.Opaque | ControlStyles.ResizeRedraw, true);
|
||
|
||
this.FormBorderStyle = FormBorderStyle.None;
|
||
this.DoubleBuffered = false;
|
||
|
||
_res = new System.ComponentModel.ComponentResourceManager (typeof (ModernForm));
|
||
|
||
UpdateDPIValues ();
|
||
LoadResourceImages ();
|
||
ScaleButtonImages ();
|
||
EnableDWM ();
|
||
}
|
||
|
||
// ==================== DPI 处理 ====================
|
||
private void UpdateDPIValues ()
|
||
{
|
||
double dpiScale = UITheme.DPIDouble;
|
||
_titleBarHeight = (int)Math.Round (30 * dpiScale);
|
||
_borderWidth = (int)Math.Round (1 * dpiScale);
|
||
_iconSize = (int)Math.Round (24 * dpiScale);
|
||
_iconOffsetLeft = (int)Math.Round (3 * dpiScale);
|
||
_iconOffsetTop = (int)Math.Round (3 * dpiScale);
|
||
_iconSize = _titleBarHeight - _iconOffsetTop * 2;
|
||
|
||
// 按钮显示大小与标题栏等高
|
||
_buttonDisplaySize = _titleBarHeight;
|
||
|
||
this.Padding = new Padding (_borderWidth, _titleBarHeight, _borderWidth, _borderWidth);
|
||
}
|
||
|
||
// ==================== DWM 适配 ====================
|
||
private void EnableDWM ()
|
||
{
|
||
try
|
||
{
|
||
// 检查 DWM 是否启用
|
||
bool isDwmEnabled = false;
|
||
if (DwmIsCompositionEnabled (out isDwmEnabled) == 0 && isDwmEnabled)
|
||
{
|
||
// 仅禁用标题栏 DWM 绘制,保留 Aero 特性(Aero Snap、Aero Shake 等)
|
||
MARGINS margins = new MARGINS
|
||
{
|
||
cxLeftWidth = 0,
|
||
cxRightWidth = 0,
|
||
cyTopHeight = 0,
|
||
cyBottomHeight = 0
|
||
};
|
||
|
||
DwmExtendFrameIntoClientArea (this.Handle, ref margins);
|
||
}
|
||
}
|
||
catch
|
||
{
|
||
// DWM 不可用,继续正常运行
|
||
}
|
||
}
|
||
|
||
// ==================== 资源加载 ====================
|
||
private void LoadResourceImages ()
|
||
{
|
||
try
|
||
{
|
||
// 最小化按钮
|
||
_minNormal = _res.GetObject ("min") as Bitmap;
|
||
_minLight = _res.GetObject ("min_light") as Bitmap;
|
||
_minPress = _res.GetObject ("min_press") as Bitmap;
|
||
|
||
// 最大化按钮
|
||
_maxNormal = _res.GetObject ("max") as Bitmap;
|
||
_maxLight = _res.GetObject ("max_light") as Bitmap;
|
||
_maxPress = _res.GetObject ("max_press") as Bitmap;
|
||
|
||
// 还原按钮
|
||
_restoreNormal = _res.GetObject ("restore") as Bitmap;
|
||
_restoreLight = _res.GetObject ("restore_light") as Bitmap;
|
||
_restorePress = _res.GetObject ("restore_press") as Bitmap;
|
||
|
||
// 关闭按钮
|
||
_cancelNormal = _res.GetObject ("cancel") as Bitmap;
|
||
_cancelLight = _res.GetObject ("cancel_light") as Bitmap;
|
||
_cancelPress = _res.GetObject ("cancel_press") as Bitmap;
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
MessageBox.Show ("加载资源失败: " + ex.Message);
|
||
}
|
||
}
|
||
|
||
// ==================== 按钮图片 DPI 缩放 ====================
|
||
private void ScaleButtonImages ()
|
||
{
|
||
double dpiScale = UITheme.DPIDouble;
|
||
int scaledSize = _buttonDisplaySize;
|
||
|
||
try
|
||
{
|
||
// 最小化按钮
|
||
_minNormalScaled = ScaleBitmap (_minNormal, scaledSize);
|
||
_minLightScaled = ScaleBitmap (_minLight, scaledSize);
|
||
_minPressScaled = ScaleBitmap (_minPress, scaledSize);
|
||
|
||
// 最大化按钮
|
||
_maxNormalScaled = ScaleBitmap (_maxNormal, scaledSize);
|
||
_maxLightScaled = ScaleBitmap (_maxLight, scaledSize);
|
||
_maxPressScaled = ScaleBitmap (_maxPress, scaledSize);
|
||
|
||
// 还原按钮
|
||
_restoreNormalScaled = ScaleBitmap (_restoreNormal, scaledSize);
|
||
_restoreLightScaled = ScaleBitmap (_restoreLight, scaledSize);
|
||
_restorePressScaled = ScaleBitmap (_restorePress, scaledSize);
|
||
|
||
// 关闭按钮
|
||
_cancelNormalScaled = ScaleBitmap (_cancelNormal, scaledSize);
|
||
_cancelLightScaled = ScaleBitmap (_cancelLight, scaledSize);
|
||
_cancelPressScaled = ScaleBitmap (_cancelPress, scaledSize);
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
MessageBox.Show ("缩放按钮图片失败: " + ex.Message);
|
||
}
|
||
}
|
||
|
||
private Bitmap ScaleBitmap (Bitmap original, int targetSize)
|
||
{
|
||
if (original == null)
|
||
return null;
|
||
|
||
if (original.Width == targetSize && original.Height == targetSize)
|
||
return original;
|
||
|
||
Bitmap scaled = new Bitmap (targetSize, targetSize);
|
||
using (Graphics g = Graphics.FromImage (scaled))
|
||
{
|
||
g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic;
|
||
g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;
|
||
g.DrawImage (original, 0, 0, targetSize, targetSize);
|
||
}
|
||
return scaled;
|
||
}
|
||
|
||
// ==================== 属性 ====================
|
||
public Color TitleBarColorActive
|
||
{
|
||
get { return _titleBarColorActive; }
|
||
set { _titleBarColorActive = value; Invalidate (); }
|
||
}
|
||
|
||
public int ResizeAreaWidth
|
||
{
|
||
get { return _resizeAreaWidth; }
|
||
set { _resizeAreaWidth = value; }
|
||
}
|
||
|
||
public virtual Icon WindowIcon
|
||
{
|
||
get { return _windowIcon; }
|
||
set
|
||
{
|
||
_windowIcon = value;
|
||
if (this.ClientSize.Width > 0)
|
||
SendMessage (this.Handle, WM_NCPAINT, IntPtr.Zero, IntPtr.Zero);
|
||
}
|
||
}
|
||
|
||
// ==================== 坐标计算 ====================
|
||
private int GetTitleBarWidth ()
|
||
{
|
||
RECT rcWindow;
|
||
GetWindowRect (this.Handle, out rcWindow);
|
||
return rcWindow.right - rcWindow.left - _borderWidth * 2;
|
||
}
|
||
|
||
private void RecalculateButtonRects ()
|
||
{
|
||
int titleBarWidth = GetTitleBarWidth ();
|
||
if (titleBarWidth <= 0) return;
|
||
|
||
// 按钮与标题栏等高
|
||
int buttonY = 0; // 顶部对齐
|
||
int buttonHeight = _titleBarHeight;
|
||
|
||
// 从右到左排列按钮
|
||
int closeButtonX = _borderWidth + titleBarWidth - _buttonDisplaySize;
|
||
int maxButtonX = closeButtonX - _buttonDisplaySize;
|
||
int minButtonX = maxButtonX - _buttonDisplaySize;
|
||
|
||
_closeButtonRect = new Rectangle (closeButtonX, buttonY, _buttonDisplaySize, buttonHeight);
|
||
_maxButtonRect = new Rectangle (maxButtonX, buttonY, _buttonDisplaySize, buttonHeight);
|
||
_minButtonRect = new Rectangle (minButtonX, buttonY, _buttonDisplaySize, buttonHeight);
|
||
|
||
// 图标位置
|
||
_iconRect = new Rectangle (
|
||
_borderWidth + _iconOffsetLeft,
|
||
_borderWidth + (_titleBarHeight - _iconSize) / 2,
|
||
_iconSize,
|
||
_iconSize);
|
||
|
||
_buttonRectsCalculated = true;
|
||
}
|
||
|
||
// ==================== 坐标工具方法 ====================
|
||
private Point GetWindowCursorPosition ()
|
||
{
|
||
RECT rcWindow;
|
||
GetWindowRect (this.Handle, out rcWindow);
|
||
|
||
int screenX = Control.MousePosition.X;
|
||
int screenY = Control.MousePosition.Y;
|
||
|
||
int windowX = screenX - rcWindow.left;
|
||
int windowY = screenY - rcWindow.top;
|
||
|
||
return new Point (windowX, windowY);
|
||
}
|
||
|
||
private void UpdateButtonHoverState (Point windowPt)
|
||
{
|
||
if (!_buttonRectsCalculated)
|
||
RecalculateButtonRects ();
|
||
|
||
// 保存原来的状态
|
||
ButtonState oldMin = _minState;
|
||
ButtonState oldMax = _maxState;
|
||
ButtonState oldClose = _closeState;
|
||
|
||
// 更新 hover 状态
|
||
_isMouseOverMinButton = _minButtonRect.Contains (windowPt);
|
||
_isMouseOverMaxButton = _maxButtonRect.Contains (windowPt);
|
||
_isMouseOverCloseButton = _closeButtonRect.Contains (windowPt);
|
||
|
||
// 更新 ButtonState
|
||
_minState = _isMinButtonPressed ? ButtonState.Pressed : (_isMouseOverMinButton ? ButtonState.Hover : ButtonState.Normal);
|
||
_maxState = _isMaxButtonPressed ? ButtonState.Pressed : (_isMouseOverMaxButton ? ButtonState.Hover : ButtonState.Normal);
|
||
_closeState = _isCloseButtonPressed ? ButtonState.Pressed : (_isMouseOverCloseButton ? ButtonState.Hover : ButtonState.Normal);
|
||
|
||
// 如果状态变化,触发重绘
|
||
if (!_isInNCPaint &&
|
||
(oldMin != _minState || oldMax != _maxState || oldClose != _closeState))
|
||
{
|
||
SendMessage (this.Handle, WM_NCPAINT, IntPtr.Zero, IntPtr.Zero);
|
||
}
|
||
}
|
||
|
||
|
||
|
||
// ==================== WndProc 处理 ====================
|
||
protected override void WndProc (ref Message m)
|
||
{
|
||
switch (m.Msg)
|
||
{
|
||
case WM_NCCALCSIZE:
|
||
HandleNCCalcSize (ref m);
|
||
return;
|
||
|
||
case WM_GETMINMAXINFO:
|
||
HandleGetMinMaxInfo (ref m);
|
||
return;
|
||
|
||
case WM_NCPAINT:
|
||
// 防止重复处理
|
||
_isInNCPaint = true;
|
||
try
|
||
{
|
||
PaintNonClientArea ();
|
||
PaintTitleBar ();
|
||
m.Result = IntPtr.Zero;
|
||
}
|
||
finally
|
||
{
|
||
_isInNCPaint = false;
|
||
}
|
||
return;
|
||
|
||
case WM_NCACTIVATE:
|
||
_isFormActive = m.WParam.ToInt32 () != 0;
|
||
base.WndProc (ref m);
|
||
// 延迟重绘以避免闪烁
|
||
if (!_isInNCPaint)
|
||
SendMessage (this.Handle, WM_NCPAINT, IntPtr.Zero, IntPtr.Zero);
|
||
return;
|
||
|
||
case WM_PAINT:
|
||
base.WndProc (ref m);
|
||
return;
|
||
|
||
case WM_SIZE:
|
||
base.WndProc (ref m);
|
||
_buttonRectsCalculated = false;
|
||
Invalidate ();
|
||
if (!_isInNCPaint)
|
||
SendMessage (this.Handle, WM_NCPAINT, IntPtr.Zero, IntPtr.Zero);
|
||
return;
|
||
|
||
case WM_NCHITTEST:
|
||
HandleNCHitTest (ref m);
|
||
return;
|
||
|
||
case WM_MOUSEMOVE:
|
||
HandleMouseMove (m);
|
||
base.WndProc (ref m);
|
||
return;
|
||
|
||
case WM_NCLBUTTONDOWN:
|
||
HandleNCMouseDown (m);
|
||
return;
|
||
|
||
case WM_NCLBUTTONUP:
|
||
HandleNCMouseUp (m);
|
||
return;
|
||
|
||
case WM_MOUSELEAVE:
|
||
HandleMouseLeave (m);
|
||
base.WndProc (ref m);
|
||
return;
|
||
|
||
default:
|
||
base.WndProc (ref m);
|
||
return;
|
||
}
|
||
}
|
||
|
||
private void HandleNCCalcSize (ref Message m)
|
||
{
|
||
if (m.WParam != IntPtr.Zero)
|
||
{
|
||
NCCALCSIZE_PARAMS nccs = (NCCALCSIZE_PARAMS)Marshal.PtrToStructure (m.LParam, typeof (NCCALCSIZE_PARAMS));
|
||
|
||
// 调整客户区以留出边框空间
|
||
nccs.rgrc [0].left += _borderWidth;
|
||
nccs.rgrc [0].right -= _borderWidth;
|
||
nccs.rgrc [0].bottom -= _borderWidth;
|
||
// 关键:不修改顶部,让非客户区承载标题栏
|
||
// nccs.rgrc [0].top 保持不变
|
||
|
||
Marshal.StructureToPtr (nccs, m.LParam, false);
|
||
}
|
||
m.Result = IntPtr.Zero;
|
||
}
|
||
|
||
private void HandleGetMinMaxInfo (ref Message m)
|
||
{
|
||
MINMAXINFO mmi = (MINMAXINFO)Marshal.PtrToStructure (m.LParam, typeof (MINMAXINFO));
|
||
|
||
IntPtr hMonitor = MonitorFromWindow (this.Handle, MONITOR_DEFAULTTONULL);
|
||
if (hMonitor != IntPtr.Zero)
|
||
{
|
||
MONITORINFO monitorInfo = new MONITORINFO ();
|
||
monitorInfo.cbSize = Marshal.SizeOf (monitorInfo);
|
||
if (GetMonitorInfo (hMonitor, ref monitorInfo))
|
||
{
|
||
int workWidth = monitorInfo.rcWork.right - monitorInfo.rcWork.left;
|
||
int workHeight = monitorInfo.rcWork.bottom - monitorInfo.rcWork.top;
|
||
|
||
mmi.ptMaxSize.X = workWidth + _borderWidth * 2;
|
||
mmi.ptMaxSize.Y = workHeight + _borderWidth;
|
||
|
||
mmi.ptMaxPosition.X = monitorInfo.rcWork.left - _borderWidth;
|
||
mmi.ptMaxPosition.Y = monitorInfo.rcWork.top;
|
||
}
|
||
}
|
||
|
||
Marshal.StructureToPtr (mmi, m.LParam, false);
|
||
}
|
||
|
||
private void HandleNCHitTest (ref Message m)
|
||
{
|
||
if (!_buttonRectsCalculated)
|
||
RecalculateButtonRects ();
|
||
|
||
Point windowPt = GetWindowCursorPosition ();
|
||
|
||
RECT rcWindow;
|
||
GetWindowRect (this.Handle, out rcWindow);
|
||
int windowWidth = rcWindow.right - rcWindow.left;
|
||
int windowHeight = rcWindow.bottom - rcWindow.top;
|
||
|
||
int corner = _resizeAreaWidth;
|
||
|
||
// 四角
|
||
if (windowPt.X < corner && windowPt.Y < corner) { m.Result = (IntPtr)HTTOPLEFT; return; }
|
||
if (windowPt.X > windowWidth - corner && windowPt.Y < corner) { m.Result = (IntPtr)HTTOPRIGHT; return; }
|
||
if (windowPt.X < corner && windowPt.Y > windowHeight - corner) { m.Result = (IntPtr)HTBOTTOMLEFT; return; }
|
||
if (windowPt.X > windowWidth - corner && windowPt.Y > windowHeight - corner) { m.Result = (IntPtr)HTBOTTOMRIGHT; return; }
|
||
|
||
// 边界
|
||
if (windowPt.X < corner) { m.Result = (IntPtr)HTLEFT; return; }
|
||
if (windowPt.X > windowWidth - corner) { m.Result = (IntPtr)HTRIGHT; return; }
|
||
if (windowPt.Y > windowHeight - corner) { m.Result = (IntPtr)HTBOTTOM; return; }
|
||
|
||
// 标题栏区域
|
||
if (windowPt.Y < _borderWidth + _titleBarHeight)
|
||
{
|
||
UpdateButtonHoverState (windowPt);
|
||
m.Result = (IntPtr)HTCAPTION;
|
||
return;
|
||
}
|
||
|
||
m.Result = (IntPtr)HTCLIENT;
|
||
}
|
||
|
||
private void HandleMouseMove (Message m)
|
||
{
|
||
if (!_buttonRectsCalculated)
|
||
RecalculateButtonRects ();
|
||
|
||
Point windowPt = GetWindowCursorPosition ();
|
||
|
||
// 启用 WM_MOUSELEAVE
|
||
if (!_trackingMouse)
|
||
{
|
||
TRACKMOUSEEVENT tme = new TRACKMOUSEEVENT ();
|
||
tme.cbSize = Marshal.SizeOf (tme);
|
||
tme.dwFlags = TME_LEAVE;
|
||
tme.hwndTrack = this.Handle;
|
||
TrackMouseEvent (ref tme);
|
||
_trackingMouse = true;
|
||
}
|
||
|
||
bool oldOverMin = _isMouseOverMinButton;
|
||
bool oldOverMax = _isMouseOverMaxButton;
|
||
bool oldOverClose = _isMouseOverCloseButton;
|
||
|
||
_isMouseOverMinButton = _minButtonRect.Contains (windowPt);
|
||
_isMouseOverMaxButton = _maxButtonRect.Contains (windowPt);
|
||
_isMouseOverCloseButton = _closeButtonRect.Contains (windowPt);
|
||
|
||
// 只要状态变化就重绘标题栏
|
||
if (!_isInNCPaint &&
|
||
(oldOverMin != _isMouseOverMinButton ||
|
||
oldOverMax != _isMouseOverMaxButton ||
|
||
oldOverClose != _isMouseOverCloseButton))
|
||
{
|
||
SendMessage (this.Handle, WM_NCPAINT, IntPtr.Zero, IntPtr.Zero);
|
||
}
|
||
|
||
base.WndProc (ref m); // 保留鼠标移动默认处理
|
||
}
|
||
|
||
|
||
private void HandleNCMouseDown (Message m)
|
||
{
|
||
if (!_buttonRectsCalculated)
|
||
RecalculateButtonRects ();
|
||
|
||
Point windowPt = GetWindowCursorPosition ();
|
||
|
||
if (_minButtonRect.Contains (windowPt))
|
||
{
|
||
_isMinButtonPressed = true;
|
||
_minState = ButtonState.Pressed;
|
||
if (!_isInNCPaint)
|
||
SendMessage (this.Handle, WM_NCPAINT, IntPtr.Zero, IntPtr.Zero);
|
||
}
|
||
else if (_maxButtonRect.Contains (windowPt))
|
||
{
|
||
_isMaxButtonPressed = true;
|
||
_maxState = ButtonState.Pressed;
|
||
if (!_isInNCPaint)
|
||
SendMessage (this.Handle, WM_NCPAINT, IntPtr.Zero, IntPtr.Zero);
|
||
}
|
||
else if (_closeButtonRect.Contains (windowPt))
|
||
{
|
||
_isCloseButtonPressed = true;
|
||
_closeState = ButtonState.Pressed;
|
||
if (!_isInNCPaint)
|
||
SendMessage (this.Handle, WM_NCPAINT, IntPtr.Zero, IntPtr.Zero);
|
||
}
|
||
else
|
||
{
|
||
base.WndProc (ref m);
|
||
}
|
||
}
|
||
|
||
private void HandleNCMouseUp (Message m)
|
||
{
|
||
if (!_buttonRectsCalculated)
|
||
RecalculateButtonRects ();
|
||
|
||
Point windowPt = GetWindowCursorPosition ();
|
||
|
||
if (_minButtonRect.Contains (windowPt) && _isMinButtonPressed)
|
||
{
|
||
this.WindowState = FormWindowState.Minimized;
|
||
}
|
||
else if (_maxButtonRect.Contains (windowPt) && _isMaxButtonPressed)
|
||
{
|
||
this.WindowState = this.WindowState == FormWindowState.Maximized
|
||
? FormWindowState.Normal
|
||
: FormWindowState.Maximized;
|
||
}
|
||
else if (_closeButtonRect.Contains (windowPt) && _isCloseButtonPressed)
|
||
{
|
||
_isMinButtonPressed = false;
|
||
_isMaxButtonPressed = false;
|
||
_isCloseButtonPressed = false;
|
||
|
||
_minState = ButtonState.Normal;
|
||
_maxState = ButtonState.Normal;
|
||
_closeState = ButtonState.Normal;
|
||
|
||
if (!_isInNCPaint)
|
||
SendMessage (this.Handle, WM_NCPAINT, IntPtr.Zero, IntPtr.Zero);
|
||
this.Close ();
|
||
return;
|
||
}
|
||
|
||
_isMinButtonPressed = false;
|
||
_isMaxButtonPressed = false;
|
||
_isCloseButtonPressed = false;
|
||
|
||
_minState = ButtonState.Normal;
|
||
_maxState = ButtonState.Normal;
|
||
_closeState = ButtonState.Normal;
|
||
|
||
if (!_isInNCPaint)
|
||
SendMessage (this.Handle, WM_NCPAINT, IntPtr.Zero, IntPtr.Zero);
|
||
}
|
||
|
||
private void HandleMouseLeave (Message m)
|
||
{
|
||
bool needRedraw = _isMouseOverMinButton || _isMouseOverMaxButton || _isMouseOverCloseButton;
|
||
|
||
_isMouseOverMinButton = false;
|
||
_isMouseOverMaxButton = false;
|
||
_isMouseOverCloseButton = false;
|
||
|
||
_minState = _isMinButtonPressed ? ButtonState.Pressed : ButtonState.Normal;
|
||
_maxState = _isMaxButtonPressed ? ButtonState.Pressed : ButtonState.Normal;
|
||
_closeState = _isCloseButtonPressed ? ButtonState.Pressed : ButtonState.Normal;
|
||
|
||
if (needRedraw && !_isInNCPaint)
|
||
{
|
||
SendMessage (this.Handle, WM_NCPAINT, IntPtr.Zero, IntPtr.Zero);
|
||
}
|
||
|
||
_trackingMouse = false;
|
||
}
|
||
|
||
// ==================== 绘制方法 ====================
|
||
private void PaintNonClientArea ()
|
||
{
|
||
IntPtr hdc = GetWindowDC (this.Handle);
|
||
if (hdc == IntPtr.Zero) return;
|
||
|
||
try
|
||
{
|
||
using (Graphics g = Graphics.FromHdc (hdc))
|
||
{
|
||
g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;
|
||
g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic;
|
||
|
||
RECT rcWindow;
|
||
GetWindowRect (this.Handle, out rcWindow);
|
||
|
||
int windowWidth = rcWindow.right - rcWindow.left;
|
||
int windowHeight = rcWindow.bottom - rcWindow.top;
|
||
|
||
// 边框颜色
|
||
Color borderColor = _isFormActive ? UITheme.GetDwmThemeColor () : Color.FromArgb (177, 177, 177);
|
||
borderColor = UITheme.GetDwmThemeColor ();
|
||
int borderW = Math.Max (1, _borderWidth);
|
||
|
||
// 绘制边框(左、右、底)
|
||
using (Pen pen = new Pen (borderColor, borderW))
|
||
{
|
||
float halfBorder = borderW / 2.0f;
|
||
|
||
// 左边框
|
||
g.DrawLine (pen, halfBorder, 0, halfBorder, windowHeight);
|
||
|
||
// 右边框
|
||
g.DrawLine (pen, windowWidth - halfBorder, 0, windowWidth - halfBorder, windowHeight);
|
||
|
||
// 底部边框
|
||
g.DrawLine (pen, 0, windowHeight - halfBorder, windowWidth, windowHeight - halfBorder);
|
||
}
|
||
}
|
||
}
|
||
finally
|
||
{
|
||
ReleaseDC (this.Handle, hdc);
|
||
}
|
||
}
|
||
|
||
private void PaintTitleBar ()
|
||
{
|
||
if (this.ClientSize.Width <= 0 || _titleBarHeight <= 0)
|
||
return;
|
||
|
||
if (!_buttonRectsCalculated)
|
||
{
|
||
RecalculateButtonRects ();
|
||
}
|
||
|
||
IntPtr hdc = GetWindowDC (this.Handle);
|
||
if (hdc == IntPtr.Zero) return;
|
||
|
||
try
|
||
{
|
||
using (Graphics g = Graphics.FromHdc (hdc))
|
||
{
|
||
g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;
|
||
g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic;
|
||
|
||
RECT rcWindow;
|
||
GetWindowRect (this.Handle, out rcWindow);
|
||
int windowWidth = rcWindow.right - rcWindow.left;
|
||
|
||
// 标题栏绘制区域
|
||
int titleBarX = _borderWidth;
|
||
int titleBarY = 0;
|
||
int titleBarWidth = windowWidth - _borderWidth * 2;
|
||
int titleBarHeight = _titleBarHeight;
|
||
Color titleBarColor = _isFormActive ? _titleBarColorActive : Color.FromArgb (177, 177, 177);
|
||
// 绘制标题栏背景(完整填充,不留缝隙)
|
||
using (Brush brush = new SolidBrush (titleBarColor))
|
||
{
|
||
g.FillRectangle (brush, titleBarX, titleBarY, titleBarWidth, titleBarHeight);
|
||
}
|
||
|
||
// 绘制图标
|
||
Icon iconToDraw = _windowIcon ?? this.Icon;
|
||
if (iconToDraw != null && _iconRect.Width > 0 && _iconRect.Height > 0)
|
||
{
|
||
try
|
||
{
|
||
Bitmap iconBitmap = iconToDraw.ToBitmap ();
|
||
Rectangle adjustedIconRect = new Rectangle (
|
||
_iconRect.X,
|
||
_iconRect.Y,
|
||
_iconSize,
|
||
_iconSize);
|
||
g.DrawImage (iconBitmap, adjustedIconRect);
|
||
iconBitmap.Dispose ();
|
||
}
|
||
catch { }
|
||
}
|
||
|
||
// 绘制标题文本
|
||
if (!string.IsNullOrEmpty (this.Text))
|
||
{
|
||
using (Font font = GetTitleFont ())
|
||
{
|
||
SizeF textSize = g.MeasureString (this.Text, font);
|
||
float textX = titleBarX + (titleBarWidth - textSize.Width) / 2.0f;
|
||
float textY = titleBarY + (titleBarHeight - textSize.Height) / 2.0f;
|
||
|
||
using (Brush textBrush = new SolidBrush (Color.White))
|
||
{
|
||
g.DrawString (this.Text, font, textBrush, textX, textY);
|
||
}
|
||
}
|
||
}
|
||
|
||
// 绘制按钮
|
||
DrawTitleBarButtons (g);
|
||
}
|
||
}
|
||
finally
|
||
{
|
||
ReleaseDC (this.Handle, hdc);
|
||
}
|
||
}
|
||
|
||
private Font GetTitleFont ()
|
||
{
|
||
string [] fontNames = { "Microsoft YaHei", "Segoe UI" };
|
||
|
||
foreach (string fontName in fontNames)
|
||
{
|
||
try
|
||
{
|
||
using (Font testFont = new Font (fontName, 11, FontStyle.Regular))
|
||
{
|
||
return new Font (fontName, 11, FontStyle.Regular);
|
||
}
|
||
}
|
||
catch
|
||
{
|
||
}
|
||
}
|
||
|
||
return new Font (SystemFonts.DefaultFont ?? new Font ("Arial", 11, FontStyle.Regular), FontStyle.Regular);
|
||
}
|
||
|
||
private void DrawTitleBarButtons (Graphics g)
|
||
{
|
||
Color backgroundColor = _titleBarColorActive;
|
||
|
||
// 绘制最小化按钮
|
||
DrawButton (g, _minButtonRect, _minState, _minNormalScaled, _minLightScaled, _minPressScaled, backgroundColor);
|
||
|
||
// 绘制最大化/还原按钮
|
||
if (this.WindowState == FormWindowState.Maximized)
|
||
{
|
||
DrawButton (g, _maxButtonRect, _maxState, _restoreNormalScaled, _restoreLightScaled, _restorePressScaled, backgroundColor);
|
||
}
|
||
else
|
||
{
|
||
DrawButton (g, _maxButtonRect, _maxState, _maxNormalScaled, _maxLightScaled, _maxPressScaled, backgroundColor);
|
||
}
|
||
|
||
// 绘制关闭按钮
|
||
DrawButton (g, _closeButtonRect, _closeState, _cancelNormalScaled, _cancelLightScaled, _cancelPressScaled, backgroundColor);
|
||
}
|
||
|
||
private void DrawButton (Graphics g, Rectangle buttonRect, ButtonState state,
|
||
Bitmap imgNormal, Bitmap imgLight, Bitmap imgPress, Color backgroundColor)
|
||
{
|
||
if (buttonRect.Width <= 0 || buttonRect.Height <= 0)
|
||
return;
|
||
|
||
// 1. 填充背景色
|
||
using (SolidBrush brush = new SolidBrush (backgroundColor))
|
||
{
|
||
g.FillRectangle (brush, buttonRect);
|
||
}
|
||
|
||
// 2. 选择状态对应的图片
|
||
Bitmap imgBitmap = null;
|
||
if (state == ButtonState.Pressed)
|
||
imgBitmap = imgPress ?? imgLight ?? imgNormal;
|
||
else if ((buttonRect == _minButtonRect && _isMouseOverMinButton) ||
|
||
(buttonRect == _maxButtonRect && _isMouseOverMaxButton) ||
|
||
(buttonRect == _closeButtonRect && _isMouseOverCloseButton))
|
||
imgBitmap = imgLight ?? imgNormal;
|
||
else
|
||
imgBitmap = imgNormal;
|
||
|
||
if (imgBitmap != null)
|
||
{
|
||
// 3. 强制转换为标准 Bitmap 并绘制到目标区域
|
||
try
|
||
{
|
||
using (Bitmap bmp = new Bitmap (imgBitmap.Width, imgBitmap.Height, System.Drawing.Imaging.PixelFormat.Format32bppArgb))
|
||
{
|
||
using (Graphics tempG = Graphics.FromImage (bmp))
|
||
{
|
||
tempG.DrawImage (imgBitmap, 0, 0, imgBitmap.Width, imgBitmap.Height);
|
||
}
|
||
|
||
// 绘制到目标区域并自动缩放
|
||
g.DrawImage (bmp, buttonRect);
|
||
}
|
||
}
|
||
catch { }
|
||
}
|
||
}
|
||
|
||
|
||
protected override CreateParams CreateParams
|
||
{
|
||
get
|
||
{
|
||
CreateParams cp = base.CreateParams;
|
||
cp.ClassStyle |= CS_DROPSHADOW;
|
||
return cp;
|
||
}
|
||
}
|
||
|
||
protected override void Dispose (bool disposing)
|
||
{
|
||
if (disposing)
|
||
{
|
||
// 原始资源
|
||
_minNormal?.Dispose ();
|
||
_minLight?.Dispose ();
|
||
_minPress?.Dispose ();
|
||
_maxNormal?.Dispose ();
|
||
_maxLight?.Dispose ();
|
||
_maxPress?.Dispose ();
|
||
_restoreNormal?.Dispose ();
|
||
_restoreLight?.Dispose ();
|
||
_restorePress?.Dispose ();
|
||
_cancelNormal?.Dispose ();
|
||
_cancelLight?.Dispose ();
|
||
_cancelPress?.Dispose ();
|
||
|
||
// 缩放后的资源
|
||
_minNormalScaled?.Dispose ();
|
||
_minLightScaled?.Dispose ();
|
||
_minPressScaled?.Dispose ();
|
||
_maxNormalScaled?.Dispose ();
|
||
_maxLightScaled?.Dispose ();
|
||
_maxPressScaled?.Dispose ();
|
||
_restoreNormalScaled?.Dispose ();
|
||
_restoreLightScaled?.Dispose ();
|
||
_restorePressScaled?.Dispose ();
|
||
_cancelNormalScaled?.Dispose ();
|
||
_cancelLightScaled?.Dispose ();
|
||
_cancelPressScaled?.Dispose ();
|
||
}
|
||
base.Dispose (disposing);
|
||
}
|
||
}
|
||
} |