You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1181 lines
43 KiB

2 years ago
12 months ago
3 years ago
12 months ago
4 years ago
2 years ago
2 years ago
2 years ago
2 years ago
4 years ago
4 years ago
4 years ago
12 months ago
  1. using Apewer.Internals.Interop;
  2. using System;
  3. using System.Collections.Generic;
  4. using System.Drawing;
  5. using System.IO;
  6. using System.Runtime.InteropServices;
  7. using System.Security;
  8. using System.Reflection;
  9. using System.Diagnostics;
  10. #if NETFX || NETCORE
  11. using Microsoft.Win32;
  12. using System.Drawing.Drawing2D;
  13. using System.Drawing.Imaging;
  14. using System.Drawing.Text;
  15. using System.Windows.Forms;
  16. #endif
  17. using static Apewer.Internals.Interop.SHCore;
  18. namespace Apewer.Surface
  19. {
  20. /// <summary>窗体实用工具。</summary>
  21. [SecuritySafeCritical]
  22. public static class FormsUtility
  23. {
  24. /// <summary>线程锁。</summary>
  25. public static object ThreadLocker = new object();
  26. /// <summary>已使用系统 DPI 设置。</summary>
  27. public static bool UsedSystemDPI { get; private set; }
  28. internal static Nullable<float> DpiScale { get; set; }
  29. #if NETFX || NETCORE
  30. /// <summary>窗体启动初始化。</summary>
  31. [STAThread]
  32. public static void StartInitialization(bool useSystemDPI = false)
  33. {
  34. Control.CheckForIllegalCrossThreadCalls = false;
  35. Application.EnableVisualStyles();
  36. Application.SetCompatibleTextRenderingDefault(false);
  37. #if NETCORE
  38. Application.SetHighDpiMode(HighDpiMode.SystemAware);
  39. #endif
  40. // Application.SetCompatibleTextRenderingDefault(true);
  41. if (useSystemDPI) UseSystemDPI();
  42. }
  43. /// <summary>使用系统的 DPI 设置。</summary>
  44. public static void UseSystemDPI()
  45. {
  46. if (UsedSystemDPI) return;
  47. UsedSystemDPI = true;
  48. #if NETCORE
  49. Application.SetHighDpiMode(HighDpiMode.PerMonitorV2);
  50. #else
  51. SetProcessDpiAwareness(PROCESS_DPI_AWARENESS.Process_System_DPI_Aware);
  52. Marshal.GetLastWin32Error();
  53. PROCESS_DPI_AWARENESS awareness;
  54. GetProcessDpiAwareness(Process.GetCurrentProcess().Handle, out awareness);
  55. Marshal.GetLastWin32Error();
  56. #endif
  57. }
  58. /// <summary>禁用跨线程调用检查。</summary>
  59. public static void CrossThread()
  60. {
  61. Control.CheckForIllegalCrossThreadCalls = false;
  62. }
  63. #endif
  64. #region 线程
  65. /// <summary>在拥有此控件的基础窗口句柄的线程上执行指定的委托。</summary>
  66. public static void Invoke(this Control control, Action action)
  67. {
  68. if (control == null) throw new ArgumentNullException(nameof(control));
  69. if (action == null) throw new ArgumentNullException(nameof(action));
  70. // control.Invoke(action as Delegate);
  71. control.Invoke(new Action(delegate ()
  72. {
  73. action.Invoke();
  74. }));
  75. }
  76. /// <summary>在创建控件的基础句柄所在线程上异步执行指定委托。</summary>
  77. /// <exception cref="ArgumentNullException" />
  78. public static IAsyncResult BeginInvoke(this Control control, Action action)
  79. {
  80. if (control == null) throw new ArgumentNullException(nameof(control));
  81. if (action == null) throw new ArgumentNullException(nameof(action));
  82. // control.BeginInvoke(action as Delegate);
  83. return control.BeginInvoke(new Action(delegate ()
  84. {
  85. action.Invoke();
  86. }));
  87. }
  88. /// <summary>控件属于当前线程。</summary>
  89. /// <exception cref="ArgumentNullException" />
  90. public static bool OnCurrentThread(Control control)
  91. {
  92. if (control == null) throw new ArgumentNullException(nameof(control));
  93. var controlThreadId = User32.GetWindowThreadProcessId(new HandleRef(control, control.Handle), out int _);
  94. var currentThreadId = Kernel32.GetCurrentThreadId();
  95. return controlThreadId == currentThreadId;
  96. }
  97. #endregion
  98. #region 颜色。
  99. /// <summary>获取所有可枚举的颜色。</summary>
  100. public static Color[] EnumerableColor
  101. {
  102. get
  103. {
  104. var list = new List<Color>();
  105. list.Add(Color.AliceBlue);
  106. list.Add(Color.AntiqueWhite);
  107. list.Add(Color.Aqua);
  108. list.Add(Color.Aquamarine);
  109. list.Add(Color.Azure);
  110. list.Add(Color.Beige);
  111. list.Add(Color.Bisque);
  112. list.Add(Color.Black);
  113. list.Add(Color.BlanchedAlmond);
  114. list.Add(Color.Blue);
  115. list.Add(Color.BlueViolet);
  116. list.Add(Color.Brown);
  117. list.Add(Color.BurlyWood);
  118. list.Add(Color.CadetBlue);
  119. list.Add(Color.Chartreuse);
  120. list.Add(Color.Chocolate);
  121. list.Add(Color.Coral);
  122. list.Add(Color.CornflowerBlue);
  123. list.Add(Color.Cornsilk);
  124. list.Add(Color.Crimson);
  125. list.Add(Color.Cyan);
  126. list.Add(Color.DarkBlue);
  127. list.Add(Color.DarkCyan);
  128. list.Add(Color.DarkGoldenrod);
  129. list.Add(Color.DarkGray);
  130. list.Add(Color.DarkGreen);
  131. list.Add(Color.DarkKhaki);
  132. list.Add(Color.DarkMagenta);
  133. list.Add(Color.DarkOliveGreen);
  134. list.Add(Color.DarkOrange);
  135. list.Add(Color.DarkOrchid);
  136. list.Add(Color.DarkRed);
  137. list.Add(Color.DarkSalmon);
  138. list.Add(Color.DarkSeaGreen);
  139. list.Add(Color.DarkSlateBlue);
  140. list.Add(Color.DarkSlateGray);
  141. list.Add(Color.DarkTurquoise);
  142. list.Add(Color.DarkViolet);
  143. list.Add(Color.DeepPink);
  144. list.Add(Color.DeepSkyBlue);
  145. list.Add(Color.DimGray);
  146. list.Add(Color.DodgerBlue);
  147. list.Add(Color.Firebrick);
  148. list.Add(Color.FloralWhite);
  149. list.Add(Color.ForestGreen);
  150. list.Add(Color.Fuchsia);
  151. list.Add(Color.Gainsboro);
  152. list.Add(Color.GhostWhite);
  153. list.Add(Color.Gold);
  154. list.Add(Color.Goldenrod);
  155. list.Add(Color.Gray);
  156. list.Add(Color.Green);
  157. list.Add(Color.GreenYellow);
  158. list.Add(Color.Honeydew);
  159. list.Add(Color.HotPink);
  160. list.Add(Color.IndianRed);
  161. list.Add(Color.Indigo);
  162. list.Add(Color.Ivory);
  163. list.Add(Color.Khaki);
  164. list.Add(Color.Lavender);
  165. list.Add(Color.LavenderBlush);
  166. list.Add(Color.LawnGreen);
  167. list.Add(Color.LemonChiffon);
  168. list.Add(Color.LightBlue);
  169. list.Add(Color.LightCoral);
  170. list.Add(Color.LightCyan);
  171. list.Add(Color.LightGoldenrodYellow);
  172. list.Add(Color.LightGray);
  173. list.Add(Color.LightGreen);
  174. list.Add(Color.LightPink);
  175. list.Add(Color.LightSalmon);
  176. list.Add(Color.LightSeaGreen);
  177. list.Add(Color.LightSkyBlue);
  178. list.Add(Color.LightSlateGray);
  179. list.Add(Color.LightSteelBlue);
  180. list.Add(Color.LightYellow);
  181. list.Add(Color.Lime);
  182. list.Add(Color.LimeGreen);
  183. list.Add(Color.Linen);
  184. list.Add(Color.Magenta);
  185. list.Add(Color.Maroon);
  186. list.Add(Color.MediumAquamarine);
  187. list.Add(Color.MediumBlue);
  188. list.Add(Color.MediumOrchid);
  189. list.Add(Color.MediumPurple);
  190. list.Add(Color.MediumSeaGreen);
  191. list.Add(Color.MediumSlateBlue);
  192. list.Add(Color.MediumSpringGreen);
  193. list.Add(Color.MediumTurquoise);
  194. list.Add(Color.MediumVioletRed);
  195. list.Add(Color.MidnightBlue);
  196. list.Add(Color.MintCream);
  197. list.Add(Color.MistyRose);
  198. list.Add(Color.Moccasin);
  199. list.Add(Color.NavajoWhite);
  200. list.Add(Color.Navy);
  201. list.Add(Color.OldLace);
  202. list.Add(Color.Olive);
  203. list.Add(Color.OliveDrab);
  204. list.Add(Color.Orange);
  205. list.Add(Color.OrangeRed);
  206. list.Add(Color.Orchid);
  207. list.Add(Color.PaleGoldenrod);
  208. list.Add(Color.PaleGreen);
  209. list.Add(Color.PaleTurquoise);
  210. list.Add(Color.PaleVioletRed);
  211. list.Add(Color.PapayaWhip);
  212. list.Add(Color.PeachPuff);
  213. list.Add(Color.Peru);
  214. list.Add(Color.Pink);
  215. list.Add(Color.Plum);
  216. list.Add(Color.PowderBlue);
  217. list.Add(Color.Purple);
  218. list.Add(Color.Red);
  219. list.Add(Color.RosyBrown);
  220. list.Add(Color.RoyalBlue);
  221. list.Add(Color.SaddleBrown);
  222. list.Add(Color.Salmon);
  223. list.Add(Color.SandyBrown);
  224. list.Add(Color.SeaGreen);
  225. list.Add(Color.SeaShell);
  226. list.Add(Color.Sienna);
  227. list.Add(Color.Silver);
  228. list.Add(Color.SkyBlue);
  229. list.Add(Color.SlateBlue);
  230. list.Add(Color.SlateGray);
  231. list.Add(Color.Snow);
  232. list.Add(Color.SpringGreen);
  233. list.Add(Color.SteelBlue);
  234. list.Add(Color.Tan);
  235. list.Add(Color.Teal);
  236. list.Add(Color.Thistle);
  237. list.Add(Color.Tomato);
  238. //list.Add(Color.Transparent);
  239. list.Add(Color.Turquoise);
  240. list.Add(Color.Violet);
  241. list.Add(Color.Wheat);
  242. list.Add(Color.White);
  243. list.Add(Color.WhiteSmoke);
  244. list.Add(Color.Yellow);
  245. list.Add(Color.YellowGreen);
  246. return list.ToArray();
  247. }
  248. }
  249. /// <summary>用于填充背景色。</summary>
  250. public static Color GraceWall { get { return Color.FromArgb(0xff, 0xf7, 0xf7, 0xf7); } }
  251. /// <summary>用于控件边框。</summary>
  252. public static Color GraceBorder { get { return Color.FromArgb(0xff, 0xdf, 0xdf, 0xdf); } }
  253. /// <summary>用于无效的文本、备注文本。</summary>
  254. public static Color GraceLocked { get { return Color.FromArgb(0xff, 0x7f, 0x7f, 0x7f); } }
  255. /// <summary>用于次级文本。</summary>
  256. public static Color GraceMinor { get { return Color.FromArgb(0xff, 0x3f, 0x3f, 0x3f); } }
  257. /// <summary>用于焦点状态的控件边框。</summary>
  258. public static Color GraceSilver { get { return Color.FromArgb(0xff, 0xbf, 0xbf, 0xbf); } }
  259. /// <summary>系统定义的颜色。</summary>
  260. public static Color Transparent { get { return Color.Transparent; } }
  261. /// <summary>系统定义的颜色。</summary>
  262. public static Color Black { get { return Color.Black; } }
  263. /// <summary>系统定义的颜色。</summary>
  264. public static Color White { get { return Color.White; } }
  265. /// <summary>系统定义的颜色。</summary>
  266. public static Color Gray { get { return Color.Gray; } }
  267. /// <summary>系统定义的颜色。</summary>
  268. public static Color DarkGray { get { return Color.DarkGray; } }
  269. /// <summary>系统定义的颜色。</summary>
  270. public static Color LightGray { get { return Color.LightGray; } }
  271. /// <summary>系统定义的颜色。</summary>
  272. public static Color Red { get { return Color.Red; } }
  273. /// <summary>系统定义的颜色。</summary>
  274. public static Color Green { get { return Color.Green; } }
  275. /// <summary>系统定义的颜色。</summary>
  276. public static Color Blue { get { return Color.Blue; } }
  277. /// <summary>系统定义的颜色。</summary>
  278. public static Color Orange { get { return Color.DarkOrange; } }
  279. /// <summary>系统定义的颜色。</summary>
  280. public static Color Purple { get { return Color.Purple; } }
  281. /// <summary>随机主题色。</summary>
  282. public static Color RandomColor
  283. {
  284. get { var colors = EnumerableColor; return colors[NumberUtility.Random(colors.Length - 1)]; }
  285. }
  286. /// <summary>计算 Alpha 合成颜色(基色),基色均需计算。</summary>
  287. /// <param name="alpha">前景色 Alpha 值。</param>
  288. /// <param name="back">背景色(基色)。</param>
  289. /// <param name="fore">前景色(基色)。</param>
  290. /// <param name="depth">颜色深度,最大值。</param>
  291. /// <returns>合成后的颜色(基色)。</returns>
  292. public static int MixAlpha(int alpha, int fore, int back, int depth = 255)
  293. {
  294. int b = back, f = fore;
  295. if (b > depth) b = depth; if (b < 0) b = 0;
  296. if (f > depth) f = depth; if (f < 0) f = 0;
  297. int v = fore * alpha / depth + back * (depth - alpha) / depth;
  298. if (v > depth) v = depth; if (v < 0) v = 0;
  299. return v;
  300. }
  301. /// <summary>按 Alpha 混合不透明的颜色。</summary>
  302. /// <param name="backColor">背景色。</param>
  303. /// <param name="foreColor">前景色。</param>
  304. /// <param name="foreAlpha">前景 Alpha 值。</param>
  305. /// <returns>混合后的颜色。</returns>
  306. public static Color MixAlpha(Color backColor, Color foreColor, int foreAlpha)
  307. {
  308. var r = MixAlpha(backColor.R, foreColor.R, foreAlpha);
  309. var g = MixAlpha(backColor.G, foreColor.G, foreAlpha);
  310. var b = MixAlpha(backColor.B, foreColor.B, foreAlpha);
  311. var a = MixAlpha(backColor.A, foreColor.A, foreAlpha);
  312. return Color.FromArgb(a, r, g, b);
  313. }
  314. #endregion
  315. #region 屏幕。
  316. #if NETFX || NETCORE
  317. /// <summary>获取屏幕的截图。</summary>
  318. public static Bitmap[] ScreenShot()
  319. {
  320. var list = new List<Bitmap>();
  321. var all = Screen.AllScreens;
  322. foreach (var screen in all)
  323. {
  324. int vw = screen.Bounds.Width;
  325. int vh = screen.Bounds.Height;
  326. int vd = screen.BitsPerPixel;
  327. Bitmap b = new Bitmap(vw, vh, PixelFormat.Format32bppArgb);
  328. Graphics g = Graphics.FromImage(b);
  329. g.CopyFromScreen(0, 0, 0, 0, new System.Drawing.Size(vw, vh));
  330. g.Dispose();
  331. list.Add(b);
  332. }
  333. return list.ToArray();
  334. }
  335. /// <summary>获取指定屏幕的截图。</summary>
  336. /// <param name="screen">屏幕号。</param>
  337. internal static Bitmap ScreenShot(int screen)
  338. {
  339. var a = Screen.AllScreens;
  340. if ((a.Length > 0) && (screen >= 0) && (screen < a.Length))
  341. {
  342. try
  343. {
  344. int w = a[screen].Bounds.Width;
  345. int h = a[screen].Bounds.Height;
  346. int d = a[screen].BitsPerPixel;
  347. Bitmap b = new Bitmap(w, h, PixelFormat.Format32bppArgb);
  348. Graphics g = Graphics.FromImage(b);
  349. g.CopyFromScreen(0, 0, 0, 0, new System.Drawing.Size(w, h));
  350. g.Dispose();
  351. return b;
  352. }
  353. catch { }
  354. }
  355. return new Bitmap(0, 0, PixelFormat.Format32bppArgb);
  356. }
  357. /// <summary>获取指定屏幕的截图。</summary>
  358. /// <param name="screen">屏幕,若为 Null 则选择主屏幕。</param>
  359. internal static Bitmap ScreenShot(Screen screen)
  360. {
  361. if (screen == null)
  362. {
  363. try
  364. {
  365. int w = Screen.PrimaryScreen.Bounds.Width;
  366. int h = Screen.PrimaryScreen.Bounds.Height;
  367. int d = Screen.PrimaryScreen.BitsPerPixel;
  368. Bitmap b = new Bitmap(w, h, PixelFormat.Format32bppArgb);
  369. Graphics g = Graphics.FromImage(b);
  370. g.CopyFromScreen(0, 0, 0, 0, new System.Drawing.Size(w, h));
  371. g.Dispose();
  372. return b;
  373. }
  374. catch { }
  375. }
  376. return new Bitmap(0, 0, PixelFormat.Format32bppArgb);
  377. }
  378. #endif
  379. #endregion
  380. #region Aero
  381. /// <summary>获取 DWM 的 Composition 启用状态。</summary>
  382. public static bool DwmIsCompositionEnabled
  383. {
  384. get => DwmApi.DwmIsCompositionEnabled();
  385. }
  386. #endregion
  387. #region 窗体。
  388. #if NETFX || NETCORE
  389. /// <summary>移动窗体。</summary>
  390. public static void MoveForm(Form form)
  391. {
  392. if (form != null) MoveForm(form.Handle);
  393. }
  394. /// <summary>移动窗体。</summary>
  395. public static void MoveForm(IntPtr form)
  396. {
  397. User32.ReleaseCapture();
  398. User32.SendMessage(form, 0x112, new IntPtr(Constant.SC_MOVE + 2), new IntPtr(0));
  399. }
  400. /// <summary>启用鼠标穿透。</summary>
  401. public static void MousePenetration(Form form)
  402. {
  403. if (form != null) MousePenetration(form.Handle);
  404. }
  405. /// <summary>启用鼠标穿透。</summary>
  406. public static void MousePenetration(IntPtr form)
  407. {
  408. int intExTemp = User32.GetWindowLong(form, Constant.GWL_EXSTYLE);
  409. int oldGWLEx = User32.SetWindowLong(form, Constant.GWL_EXSTYLE, Constant.WS_EX_TRANSPARENT | Constant.WS_EX_LAYERED);
  410. }
  411. /// <summary>将窗体最大化,不覆盖任务栏。</summary>
  412. /// <param name="form">窗体。</param>
  413. public static void ToMaximum(Form form)
  414. {
  415. if (form != null)
  416. {
  417. var s = Screen.FromControl(form);
  418. var r = s.WorkingArea;
  419. if ((form.MaximumSize.Width < r.Width) || (form.MaximumSize.Height < r.Height))
  420. {
  421. form.MaximumSize = r.Size;
  422. }
  423. form.Location = r.Location;
  424. form.Size = r.Size;
  425. }
  426. }
  427. /// <summary>用指定图像绘制窗体,窗体与图像的大小必须一致。</summary>
  428. /// <param name="form">要绘制的窗体。</param>
  429. /// <param name="image">要使用的图像。</param>
  430. public static void UpdateShadow(Form form, Image image)
  431. {
  432. if ((form != null) && (image != null))
  433. {
  434. Bitmap vbitmap;
  435. if ((form.Width != image.Width) || (form.Height != image.Height))
  436. {
  437. vbitmap = new Bitmap(image.GetThumbnailImage(form.Width, form.Height, null, IntPtr.Zero));
  438. }
  439. else
  440. {
  441. vbitmap = new Bitmap(image);
  442. }
  443. var screendc = User32.GetDC(IntPtr.Zero);
  444. var location = new Internals.Interop.Point(form.Left, form.Top);
  445. var size = new Internals.Interop.Size(form.Width, form.Height);
  446. var compatibledc = Gdi32.CreateCompatibleDC(screendc);
  447. var handle = vbitmap.GetHbitmap(Color.FromArgb(0));
  448. var oldbitmap = Gdi32.SelectObject(compatibledc, handle);
  449. var srcloc = new Internals.Interop.Point(0, 0);
  450. var blendfunction = new BlendFunction();
  451. blendfunction.blendop = Constant.AC_SRC_OVER;
  452. blendfunction.sourceconstantalpha = byte.Parse("255");
  453. blendfunction.alphaformat = Constant.AC_SRC_ALPHA;
  454. blendfunction.blendflags = 0;
  455. User32.UpdateLayeredWindow(form.Handle, screendc, ref location, ref size, compatibledc, ref srcloc, 0, ref blendfunction, Constant.ULW_ALPHA);
  456. if (handle != IntPtr.Zero)
  457. {
  458. Gdi32.SelectObject(compatibledc, oldbitmap);
  459. Gdi32.DeleteObject(handle);
  460. }
  461. User32.ReleaseDC(IntPtr.Zero, screendc);
  462. Gdi32.DeleteDC(compatibledc);
  463. }
  464. }
  465. /// <summary>为窗体启用阴影。</summary>
  466. /// <param name="form">要启用阴影的窗体。</param>
  467. public static void EnableShadow(Form form)
  468. {
  469. if (form == null) return;
  470. int vformclass = User32.GetClassLong(form.Handle, Constant.GCL_STYLE);
  471. User32.SetClassLong(form.Handle, Constant.GCL_STYLE, vformclass | Constant.CS_DROPSHADOW);
  472. }
  473. /// <summary>恢复任务栏中窗体标题的右键菜单。</summary>
  474. public static void SetTaskMenu(Form form)
  475. {
  476. if (form != null)
  477. {
  478. var hwnd = form.Handle;
  479. var href = new HandleRef(form, hwnd);
  480. int windowLong = (User32.GetWindowLong(hwnd, -16));
  481. User32.SetWindowLong(hwnd, -16, windowLong | Constant.WS_SYSMENU | Constant.WS_MINIMIZEBOX);
  482. }
  483. }
  484. #endif
  485. #endregion
  486. #region 控件。
  487. #if NETFX || NETCORE
  488. /// <summary>在主界面中移动控件,由目标控件取代当前控件,形成切换控件的动画。</summary>
  489. /// <param name="current">当前显示的控件。</param>
  490. /// <param name="target">将要显示的目标控件。</param>
  491. public static void AnimateGoPanel(Control current, Control target)
  492. {
  493. AnimateGoPanel(current.Location, current.Size, 0, 10, current, target, true);
  494. }
  495. /// <summary>在主界面中移动控件,由目标控件取代当前控件,形成切换控件的动画。</summary>
  496. /// <param name="current">当前显示的控件。</param>
  497. /// <param name="target">将要显示的目标控件。</param>
  498. /// <param name="next">向左移动。</param>
  499. public static void AnimateGoPanel(Control current, Control target, bool next)
  500. {
  501. AnimateGoPanel(current.Location, current.Size, 0, 10, current, target, next);
  502. }
  503. /// <summary>在主界面中移动控件,由目标控件取代当前控件,形成切换控件的动画。</summary>
  504. /// <param name="point">控件左上角坐标。</param>
  505. /// <param name="size">控件大小。</param>
  506. /// <param name="span">当前控件和目标控件的间距,若值小于 0 则更正为 0 。</param>
  507. /// <param name="frame">移动的次数,若值小于 0 则更正为 20。</param>
  508. /// <param name="current">当前显示的控件。</param>
  509. /// <param name="target">将要显示的目标控件。</param>
  510. /// <param name="next">向左移动。</param>
  511. public static void AnimateGoPanel(System.Drawing.Point point, System.Drawing.Size size, int span, int frame, Control current, Control target, bool next = true)
  512. {
  513. int s = (span < 0) ? span : 0;
  514. int step = (current.Width + s) / ((frame < 1) ? 20 : frame);
  515. int x = point.X;
  516. int y = point.Y;
  517. current.Size = size;
  518. target.Size = size;
  519. current.Location = point;
  520. if (next)
  521. {
  522. target.Location = new System.Drawing.Point(size.Width, point.Y);
  523. current.Visible = true;
  524. target.Visible = true;
  525. Application.DoEvents();
  526. while (target.Left > x)
  527. {
  528. s = ((target.Left - x) < step) ? (target.Left - x) : step;
  529. target.Left -= s;
  530. current.Left -= s;
  531. Application.DoEvents();
  532. System.Threading.Thread.Sleep(1);
  533. }
  534. }
  535. else
  536. {
  537. target.Location = new System.Drawing.Point(x - size.Width, x);
  538. current.Visible = true;
  539. target.Visible = true;
  540. Application.DoEvents();
  541. while (target.Left < x)
  542. {
  543. s = ((x - target.Left) < step) ? x - target.Left : step;
  544. target.Left += s;
  545. current.Left += s;
  546. Application.DoEvents();
  547. System.Threading.Thread.Sleep(1);
  548. }
  549. }
  550. current.Visible = false;
  551. }
  552. /// <summary>设置窗体置顶。</summary>
  553. public static void SetTopMost(IntPtr form, bool value = true)
  554. {
  555. if (form == null || form == IntPtr.Zero) return;
  556. User32.SetWindowPos(form, new IntPtr(value ? -1 : -2), 0, 0, 0, 0, 3);
  557. }
  558. /// <summary>设置窗体置顶。</summary>
  559. public static void SetTopMost(Form form, bool value = true)
  560. {
  561. if (form != null) SetTopMost(form.Handle, value);
  562. }
  563. /// <summary>释放系统资源。</summary>
  564. public static void Dispose(Control control)
  565. {
  566. if (control == null) return;
  567. if (control.InvokeRequired)
  568. {
  569. control.Invoke(() => Dispose(control));
  570. return;
  571. }
  572. Dispose(control.Controls);
  573. if (control is Form form)
  574. {
  575. try
  576. {
  577. if (form.Visible) form.Close();
  578. }
  579. catch { }
  580. }
  581. RuntimeUtility.Dispose(control);
  582. }
  583. /// <summary>释放系统资源。</summary>
  584. public static void Dispose(Control.ControlCollection controls)
  585. {
  586. if (controls == null) return;
  587. var count = controls.Count;
  588. if (count < 1) return;
  589. var children = new Control[count];
  590. controls.CopyTo(children, 0);
  591. foreach (var child in children)
  592. {
  593. controls.Remove(child);
  594. Dispose(child);
  595. }
  596. }
  597. #endif
  598. #endregion
  599. #region 绘图。
  600. #if NETFX || NETCORE
  601. /// <summary>绘制内边框。</summary>
  602. /// <param name="image">源图像。</param>
  603. /// <param name="border">边框颜色。</param>
  604. public static bool PaintBorder(ref Image image, Color border)
  605. {
  606. if (image != null)
  607. {
  608. var g = Graphics.FromImage(image);
  609. var p = new Pen(border);
  610. g.DrawRectangle(p, 0, 0, image.Width - 1, image.Height - 1);
  611. p.Dispose();
  612. g.Dispose();
  613. return true;
  614. }
  615. else return false;
  616. }
  617. /// <summary>绘制内边框。</summary>
  618. /// <param name="image">源图像。</param>
  619. /// <param name="border">边框颜色。</param>
  620. public static bool PaintBorder(ref Bitmap image, Color border)
  621. {
  622. if (image != null)
  623. {
  624. var g = Graphics.FromImage(image);
  625. var p = new Pen(border);
  626. g.DrawRectangle(p, 0, 0, image.Width - 1, image.Height - 1);
  627. p.Dispose();
  628. g.Dispose();
  629. return true;
  630. }
  631. else return false;
  632. }
  633. /// <summary>绘制内边框。</summary>
  634. /// <param name="image">源图像。</param>
  635. /// <param name="border">边框颜色。</param>
  636. /// <param name="wall">背景颜色。</param>
  637. public static bool PaintBorder(ref Image image, Color border, Color wall)
  638. {
  639. if (image != null)
  640. {
  641. var g = Graphics.FromImage(image);
  642. var p = new Pen(border);
  643. g.Clear(wall);
  644. g.DrawRectangle(p, 0, 0, image.Width - 1, image.Height - 1);
  645. p.Dispose();
  646. g.Dispose();
  647. return true;
  648. }
  649. else return false;
  650. }
  651. /// <summary>建立带有圆角样式的路径。</summary>
  652. /// <param name="rect">建立矩形路径的区域。</param>
  653. /// <param name="radius">圆角的大小。</param>
  654. /// <returns>建立的路径。</returns>
  655. public static GraphicsPath CreatePath(Rectangle rect, int radius = 0)
  656. {
  657. GraphicsPath path = new GraphicsPath();
  658. int r = (radius < 0) ? 0 : radius;
  659. switch (r)
  660. {
  661. case 0:
  662. path.AddRectangle(rect);
  663. break;
  664. default:
  665. path.AddArc(rect.X, rect.Y, r, r, 180, 90);
  666. path.AddArc(rect.Right - r - 1, rect.Y, r, r, 270, 90);
  667. path.AddArc(rect.Right - r - 1, rect.Bottom - r - 1, r, r, 0, 90);
  668. path.AddArc(rect.X, rect.Bottom - r - 1, r, r, 90, 90);
  669. break;
  670. }
  671. path.CloseFigure();
  672. return path;
  673. }
  674. /// <summary></summary>
  675. /// <param name="location"></param>
  676. /// <param name="size"></param>
  677. /// <param name="radius"></param>
  678. /// <returns></returns>
  679. public static GraphicsPath GetRadiusPath(PointF location, SizeF size, float radius)
  680. {
  681. var loc = location;
  682. var rds = Math.Abs(radius);
  683. var path = new GraphicsPath();
  684. float x, y, r;
  685. r = rds * 2;
  686. // 左上角。
  687. x = loc.X;
  688. y = loc.Y;
  689. path.AddArc(x, y, r, r, 180, 90);
  690. // 右上角。
  691. x = loc.X + size.Width - rds * 2;
  692. y = loc.Y;
  693. path.AddArc(x, y, r, r, 270, 90);
  694. // 左下角。
  695. x = loc.X;
  696. y = loc.Y + size.Height - rds * 2;
  697. path.AddArc(x, y, r, r, 0, 90);
  698. // 右下角。
  699. x = loc.X + size.Width - rds * 2;
  700. y = loc.Y + size.Height - rds * 2;
  701. path.AddArc(x, y, r, r, 90, 90);
  702. // 边框直线:上、下、左、右。
  703. path.AddLine((float)(loc.X + rds), (float)loc.Y, (float)(loc.X + size.Width - rds), (float)loc.Y);
  704. path.AddLine((float)(loc.X + rds), (float)(loc.Y + size.Height), (float)(loc.X + size.Width - rds), (float)(loc.Y + size.Height));
  705. path.AddLine((float)loc.X, (float)(loc.Y + rds), (float)loc.X, (float)(loc.Y + size.Height - rds));
  706. path.AddLine((float)(loc.X + size.Width), (float)(loc.Y + rds), (float)(loc.X + size.Width), (float)(loc.Y + size.Height - rds));
  707. return path;
  708. }
  709. /// <summary>获取指定文件中的图标总数。</summary>
  710. public static int GetIconsCount(string path)
  711. {
  712. if (!File.Exists(path)) return 0;
  713. var count = User32.PrivateExtractIcons(path, 0, 0, 0, null, null, 0, 0);
  714. return count;
  715. }
  716. /// <summary>获取指定文件中的图标。</summary>
  717. public static Bitmap[] GetIconsBitmap(string path = null, int size = 256)
  718. {
  719. if (string.IsNullOrEmpty(path)) path = Application.ExecutablePath;
  720. if (!File.Exists(path)) return null;
  721. var count = User32.PrivateExtractIcons(path, 0, 0, 0, null, null, 0, 0);
  722. var ptrs = new IntPtr[count];
  723. var ids = new int[count];
  724. var succeed = User32.PrivateExtractIcons(path, 0, size, size, ptrs, ids, count, 0);
  725. var images = new Bitmap[count];
  726. for (var i = 0; i < succeed; i++)
  727. {
  728. if (ptrs[i] == IntPtr.Zero) continue;
  729. using (var icon = Icon.FromHandle(ptrs[i]))
  730. {
  731. var bitmap = icon.ToBitmap();
  732. images[i] = bitmap;
  733. }
  734. User32.DestroyIcon(ptrs[i]);
  735. }
  736. return images;
  737. }
  738. /// <summary>从文件获取图标。</summary>
  739. public static Icon GetExecutableIcon(string path)
  740. {
  741. try { return Icon.ExtractAssociatedIcon(path); }
  742. catch { return null; }
  743. }
  744. #endif
  745. #endregion
  746. #region 字体。
  747. private const string FontYahei = "Microsoft Yahei";
  748. private const string FontSimsun = "Simsun";
  749. private const string GuiFontRegKey = @"HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\GRE_Initialize";
  750. private static string _fontname = "";
  751. /// <summary>存在微软雅黑字体。</summary>
  752. public static bool MsyhExist { get => File.Exists("c:\\windows\\fonts\\msyh.ttc"); }
  753. /// <summary>获取默认字体名称。</summary>
  754. public static string DefaultFontName
  755. {
  756. get
  757. {
  758. if (!string.IsNullOrEmpty(_fontname)) return _fontname;
  759. try
  760. {
  761. if (File.Exists("c:\\windows\\fonts\\msyh.ttc")) return FontYahei;
  762. else return FontSimsun;
  763. }
  764. catch { return FontSimsun; }
  765. }
  766. }
  767. /// <summary>获取默认字体名称。</summary>
  768. public static string GuiFontName { get => Registry.GetValue(GuiFontRegKey, "GUIFont.Facename", "Arial") as string; }
  769. /// <summary>获取默认字体大小。</summary>
  770. public static float GuiFontSize { get => Convert.ToSingle(Registry.GetValue(GuiFontRegKey, "GUIFont.Height", 9F)); }
  771. #if NETFX || NETCORE
  772. /// <summary>获取默认字体。</summary>
  773. public static Font GuiFont { get => new Font(GuiFontName, GuiFontSize); }
  774. /// <summary>获取默认字体。</summary>
  775. public static Font DefaultFont
  776. {
  777. get { return new Font(DefaultFontName, 9); }
  778. }
  779. /// <summary>获取大小为 9 的默认字体。</summary>
  780. public static Font NewFont()
  781. {
  782. return new Font(DefaultFontName, 9);
  783. }
  784. /// <summary>获取指定大小的默认字体。</summary>
  785. public static Font NewFont(float size)
  786. {
  787. return new Font(DefaultFontName, size);
  788. }
  789. /// <summary>获取指定大小的默认字体。</summary>
  790. public static Font NewFont(float size, bool bold)
  791. {
  792. return new Font(DefaultFontName, size, bold ? FontStyle.Bold : FontStyle.Regular);
  793. }
  794. /// <summary>从文件加载字体,并获取指定字体。</summary>
  795. /// <param name="path">文件路径。</param>
  796. /// <param name="index">文件中的 Font Family 索引,默认取第一个(索引 0)。</param>
  797. /// <returns>已存在的 Font Family,如果指定的索引处不存在 Font Family,则返回 NULL 值。</returns>
  798. public static FontFamily LoadFont(string path, int index = 0)
  799. {
  800. if (index < 0) return null;
  801. if (string.IsNullOrEmpty(path)) return null;
  802. if (!File.Exists(path)) return null;
  803. try
  804. {
  805. var pfc = new PrivateFontCollection();
  806. pfc.AddFontFile(path);
  807. var fs = pfc.Families;
  808. if (fs.Length > 0 && index < fs.Length) return fs[index];
  809. }
  810. catch { }
  811. return null;
  812. }
  813. #if NET40 || NET461
  814. /// <summary></summary>
  815. public static List<System.Windows.Media.FontFamily> ListFileFont(string path)
  816. {
  817. try
  818. {
  819. var collection = System.Windows.Media.Fonts.GetFontFamilies(path);
  820. var list = new List<System.Windows.Media.FontFamily>(collection.Count);
  821. foreach (var item in collection) list.Add(item);
  822. return list;
  823. }
  824. catch { }
  825. return new List<System.Windows.Media.FontFamily>();
  826. }
  827. /// <summary></summary>
  828. public static List<System.Windows.Media.FontFamily> ListSystemFont()
  829. {
  830. var collection = System.Windows.Media.Fonts.SystemFontFamilies;
  831. var list = new List<System.Windows.Media.FontFamily>(collection.Count);
  832. foreach (var item in collection) list.Add(item);
  833. return list;
  834. }
  835. /// <summary>枚举指定字体中的所有字符。</summary>
  836. public static List<char> EnumerateFontChars(IEnumerable<System.Windows.Media.Typeface> typefaces)
  837. {
  838. var chars = new List<char>();
  839. if (typefaces != null)
  840. {
  841. foreach (var typeface in typefaces)
  842. {
  843. var subs = EnumerateFontChars(typeface);
  844. if (subs != null) chars.AddRange(subs);
  845. }
  846. }
  847. return chars;
  848. }
  849. /// <summary>枚举指定字体中的所有字符。</summary>
  850. public static char[] EnumerateFontChars(System.Windows.Media.Typeface typeface)
  851. {
  852. System.Windows.Media.GlyphTypeface glyph;
  853. var tried = typeface.TryGetGlyphTypeface(out glyph);
  854. if (glyph == null) return null;
  855. var map = glyph.CharacterToGlyphMap;
  856. var keys = map.Keys;
  857. var chars = new List<char>(keys.Count);
  858. for (int i = 0; i < keys.Count; i++)
  859. {
  860. var index = System.Linq.Enumerable.ElementAt(keys, i);
  861. try
  862. {
  863. var c = Convert.ToChar(index);
  864. chars.Add(c);
  865. }
  866. catch { }
  867. }
  868. return chars.ToArray();
  869. }
  870. #endif
  871. /// <summary>获取已安装的字体。</summary>
  872. public static List<FontFamily> ListInstalledFonts()
  873. {
  874. var list = new List<FontFamily>();
  875. int ret = 0; // Win32 API 返回值,非零值均为异常。
  876. // 初始化 FontCollection。
  877. var collection = new Internals.FontCollection();
  878. ret = GdiPlus.GdipNewInstalledFontCollection(out collection.NativePointer);
  879. if (ret != 0) return list;
  880. // 获取字体总数。
  881. int found1 = 0;
  882. ret = GdiPlus.GdipGetFontCollectionFamilyCount(new HandleRef(collection, collection.NativePointer), out found1);
  883. if (ret != 0) return list;
  884. // 获取字体指针。
  885. var ptrs = new IntPtr[found1];
  886. int found2 = 0;
  887. ret = GdiPlus.GdipGetFontCollectionFamilyList(new HandleRef(collection, collection.NativePointer), found1, ptrs, out found2);
  888. if (ret != 0) return list;
  889. // 获取字体列表。
  890. list.Capacity = found2;
  891. for (int i = 0; i < found2; i++)
  892. {
  893. IntPtr cloned;
  894. GdiPlus.GdipCloneFontFamily(new HandleRef(null, ptrs[i]), out cloned);
  895. try
  896. {
  897. var flags = BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.CreateInstance;
  898. var args = new object[] { cloned };
  899. var instance = Activator.CreateInstance(typeof(FontFamily), flags, null, args, null, null);
  900. var item = instance as FontFamily;
  901. if (item != null) list.Add(item);
  902. }
  903. catch { }
  904. }
  905. list.Capacity = list.Count;
  906. return list;
  907. }
  908. private static bool GlyphExists(char c, Font font)
  909. {
  910. using (var dummy = Graphics.FromImage(new Bitmap(1, 1)))
  911. {
  912. IntPtr hdc = dummy.GetHdc();
  913. var pgi = new ushort[1];
  914. try
  915. {
  916. IntPtr hfont = font.ToHfont();
  917. Gdi32.SelectObject(hdc, hfont);
  918. string str = c.ToString();
  919. Gdi32.GetGlyphIndices(hdc, str, str.Length, pgi, Constant.GGI_MARK_NONEXISTING_GLYPHS);
  920. }
  921. catch { }
  922. dummy.ReleaseHdc(hdc);
  923. // 0xFFFF 表示字形不存在。
  924. return (pgi[0] != 0xffff);
  925. }
  926. }
  927. #endif
  928. #endregion
  929. #region WndProc
  930. /// <summary>允许鼠标调整窗体大小。此方法对 FormBorderStyle 为 None 的窗体生效。</summary>
  931. /// <returns>已处理事件。</returns>
  932. /// <exception cref="ArgumentNullException" />
  933. /// <exception cref="ArgumentOutOfRangeException" />
  934. public static bool AllowResizeForm(this Form form, ref Message m, int padding = 4)
  935. {
  936. if (form == null) throw new ArgumentNullException(nameof(form));
  937. if (form.FormBorderStyle != FormBorderStyle.None) return false;
  938. if (padding < 0) throw new ArgumentOutOfRangeException(nameof(padding));
  939. const int HT_LEFT = 10;
  940. const int HT_RIGHT = 11;
  941. const int HT_TOP = 12;
  942. const int HT_TOP_LEFT = 13;
  943. const int HT_TOP_RIGHT = 14;
  944. const int HT_BOTTOM = 15;
  945. const int HT_BOTTOM_LEFT = 16;
  946. const int HT_BOTTOM_RIGHT = 17;
  947. switch (m.Msg)
  948. {
  949. case 0x0084:
  950. var clientSize = form.ClientSize;
  951. var screenPoint = new System.Drawing.Point((int)m.LParam & 0xFFFF, (int)m.LParam >> 16 & 0xFFFF);
  952. var point = form.PointToClient(screenPoint);
  953. if (point.X <= padding)
  954. {
  955. if (point.Y <= padding) m.Result = (IntPtr)HT_TOP_LEFT;
  956. else if (point.Y >= clientSize.Height - padding) m.Result = (IntPtr)HT_BOTTOM_LEFT;
  957. else m.Result = (IntPtr)HT_LEFT;
  958. return true;
  959. }
  960. else if (point.X >= clientSize.Width - padding)
  961. {
  962. if (point.Y <= padding) m.Result = (IntPtr)HT_TOP_RIGHT;
  963. else if (point.Y >= clientSize.Height - padding) m.Result = (IntPtr)HT_BOTTOM_RIGHT;
  964. else m.Result = (IntPtr)HT_RIGHT;
  965. return true;
  966. }
  967. else if (point.Y <= padding)
  968. {
  969. m.Result = (IntPtr)HT_TOP;
  970. return true;
  971. }
  972. else if (point.Y >= clientSize.Height - padding)
  973. {
  974. m.Result = (IntPtr)HT_BOTTOM;
  975. return true;
  976. }
  977. break;
  978. }
  979. return false;
  980. }
  981. /// <summary>允许移动窗体。左键点击时修改消息,认为鼠标点在非客户区(标题栏)。</summary>
  982. /// <returns>已处理事件。</returns>
  983. /// <exception cref="ArgumentNullException" />
  984. public static bool AllowMoveForm(this Form form, ref Message m)
  985. {
  986. if (form == null) throw new ArgumentNullException(nameof(form));
  987. switch (m.Msg)
  988. {
  989. // 左键点击时修改消息,认为鼠标点在非客户区(标题栏)。
  990. case 0x0201:
  991. m.Msg = 0x00A1;
  992. m.LParam = IntPtr.Zero;
  993. m.WParam = new IntPtr(2);
  994. return true;
  995. }
  996. return false;
  997. }
  998. #endregion
  999. }
  1000. }