Form.cs 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384
  1. using System.Drawing;
  2. namespace System.Windows.Forms
  3. {
  4. public class Form : ContainerControl, IWin32Window
  5. {
  6. private bool isDisposed;
  7. private bool isClosing;
  8. private bool isModal;
  9. public System.Windows.Window window { get; }
  10. public static bool UseCustomWindowChrome = false;
  11. public bool IsCustomWindowChrome => window is CustomControls.ChromeWindow;
  12. private DialogResult dialogResult;
  13. public DialogResult DialogResult
  14. {
  15. get => dialogResult;
  16. set
  17. {
  18. dialogResult = value;
  19. if (value == DialogResult.None)
  20. return;
  21. if (!isClosing)
  22. window.Close();
  23. }
  24. }
  25. public override string Text
  26. {
  27. get => window.Title;
  28. set => window.Title = value;
  29. }
  30. public override int Left
  31. {
  32. get => (int)(window.Left * DpiScale);
  33. set => window.Left = value / DpiScale;
  34. }
  35. public override int Top
  36. {
  37. get => (int)(window.Top * DpiScale);
  38. set => window.Top = value / DpiScale;
  39. }
  40. public override System.Drawing.Size ClientSize
  41. {
  42. get
  43. {
  44. var delta = ((IForm)window).ClientSizeDelta;
  45. return new Drawing.Size(Width - delta.Width, Height - delta.Height);
  46. }
  47. set
  48. {
  49. var delta = ((IForm)window).ClientSizeDelta;
  50. Width = value.Width + delta.Width;
  51. Height = value.Height + delta.Height;
  52. }
  53. }
  54. public FormBorderStyle FormBorderStyle
  55. {
  56. get => ((IForm)window).FormBorderStyle;
  57. set => ((IForm)window).FormBorderStyle = value;
  58. }
  59. public bool MaximizeBox
  60. {
  61. get => ((IForm)window).MaximizeBox;
  62. set => ((IForm)window).MaximizeBox = value;
  63. }
  64. public bool MinimizeBox
  65. {
  66. get => ((IForm)window).MinimizeBox;
  67. set => ((IForm)window).MinimizeBox = value;
  68. }
  69. public bool KeyPreview { get; set; } // TODO
  70. public bool ShowIcon
  71. {
  72. get => ((IForm)window).ShowIcon;
  73. set => ((IForm)window).ShowIcon = value;
  74. }
  75. public Form MdiParent { get; set; } // TODO?
  76. private Icon icon;
  77. public Icon Icon
  78. {
  79. get => icon;
  80. set
  81. {
  82. icon = value;
  83. window.Icon = Helper.GetIcon(icon);
  84. }
  85. }
  86. public bool TopMost
  87. {
  88. get => window.Topmost;
  89. set => window.Topmost = value;
  90. }
  91. public bool ShowInTaskbar
  92. {
  93. get => window.ShowInTaskbar;
  94. set => window.ShowInTaskbar = value;
  95. }
  96. public FormStartPosition StartPosition { get; set; }
  97. private Button acceptButton;
  98. public Button AcceptButton
  99. {
  100. get => acceptButton;
  101. set
  102. {
  103. if (value != acceptButton)
  104. {
  105. if (acceptButton != null)
  106. (acceptButton.control as System.Windows.Controls.Button).IsDefault = false;
  107. if (value != null)
  108. (value.control as System.Windows.Controls.Button).IsDefault = true;
  109. acceptButton = value;
  110. }
  111. }
  112. }
  113. private Button cancelButton;
  114. public Button CancelButton
  115. {
  116. get => cancelButton;
  117. set
  118. {
  119. if (value != cancelButton)
  120. {
  121. if (cancelButton != null)
  122. (cancelButton.control as System.Windows.Controls.Button).IsCancel = true;
  123. if (value != null)
  124. (value.control as System.Windows.Controls.Button).IsCancel = true;
  125. cancelButton = value;
  126. }
  127. }
  128. }
  129. public Form Owner
  130. {
  131. get => window.Owner?.Tag as Form;
  132. set => window.Owner = value?.window;
  133. }
  134. public Control ActiveControl // TODO?
  135. {
  136. get
  137. {
  138. var el = System.Windows.Input.FocusManager.GetFocusedElement(window);
  139. if (el is System.Windows.Controls.Control ctl && ctl.Tag is Control c)
  140. return c;
  141. return null;
  142. }
  143. set
  144. {
  145. if (value != null)
  146. {
  147. value.Focus();
  148. }
  149. }
  150. }
  151. public FormWindowState WindowState
  152. {
  153. get => (FormWindowState)window.WindowState;
  154. set => window.WindowState = (Windows.WindowState)value;
  155. }
  156. public bool IsDisposed => isDisposed;
  157. public override Rectangle DisplayRectangle => new Rectangle(new System.Drawing.Point(0, 0), ClientSize);
  158. public event EventHandler Load;
  159. public event EventHandler Shown;
  160. public event EventHandler Closed;
  161. public event FormClosingEventHandler FormClosing;
  162. public event FormClosedEventHandler FormClosed;
  163. private void CenterWindowOnScreen()
  164. {
  165. IntPtr mon = NativeMethods.GetCurrentMonitorFromMousePosition();
  166. NativeMethods.RECT rect = NativeMethods.WorkAreaBoundsForMointor(mon);
  167. float dpiScale = (float)NativeMethods.GetDpiForMonitor(mon) / DeviceDpi;
  168. int newWidth = (int)(Width * dpiScale);
  169. int newHeight = (int)(Height * dpiScale);
  170. MoveWindow(rect.left + (rect.Width - newWidth) / 2, rect.top + (rect.Height - newHeight) / 2, newWidth, newHeight);
  171. }
  172. private void CenterWindowOnParent()
  173. {
  174. if (Owner != null)
  175. {
  176. // TODO: check multimonitor dpi!
  177. window.Left = (Owner.Left + (Owner.Width - Width) / 2) / Owner.DpiScale;
  178. window.Top = (Owner.Top + (Owner.Height - Height) / 2) / Owner.DpiScale;
  179. }
  180. }
  181. private void SetWindowPosition()
  182. {
  183. if (StartPosition == FormStartPosition.CenterScreen)
  184. {
  185. CenterWindowOnScreen();
  186. }
  187. else if (StartPosition == FormStartPosition.CenterParent)
  188. {
  189. CenterWindowOnParent();
  190. }
  191. }
  192. protected virtual void OnLoad(EventArgs e) => Load?.Invoke(this, e);
  193. protected virtual void OnShown(EventArgs e) => Shown?.Invoke(this, e);
  194. protected virtual void OnFormClosing(FormClosingEventArgs e) => FormClosing?.Invoke(this, e);
  195. protected virtual void OnFormClosed(FormClosedEventArgs e)
  196. {
  197. FormClosed?.Invoke(this, e);
  198. OnClosed(EventArgs.Empty);
  199. isDisposed = true;
  200. }
  201. protected virtual void OnClosed(EventArgs e) => Closed?.Invoke(this, e);
  202. protected override void Dispose(bool disposing)
  203. {
  204. base.Dispose(disposing);
  205. if (disposing)
  206. {
  207. window.Close();
  208. }
  209. }
  210. public override void ResumeLayout(bool resume)
  211. {
  212. float scale = DeviceDpi / AutoScaleDimensions.Width;
  213. if (AutoScaleMode == AutoScaleMode.Dpi || AutoScaleMode == AutoScaleMode.Font)
  214. {
  215. if (scale != 1)
  216. Scale(scale, scale);
  217. }
  218. base.ResumeLayout(resume);
  219. UpdateLayout();
  220. }
  221. public new void Show()
  222. {
  223. SetWindowPosition();
  224. OnLoad(EventArgs.Empty);
  225. isModal = false;
  226. window.Show();
  227. }
  228. public void Show(IWin32Window owner)
  229. {
  230. Owner = owner as Form;
  231. Show();
  232. }
  233. public DialogResult ShowDialog()
  234. {
  235. // this solves problem with dialog window & click on taskbar button
  236. if (Owner == null)
  237. {
  238. var ownerWindow = NativeMethods.ActiveWindow();
  239. if (ownerWindow != null && ownerWindow != window && ownerWindow.IsVisible)
  240. window.Owner = ownerWindow;
  241. }
  242. SetWindowPosition();
  243. OnLoad(EventArgs.Empty);
  244. isModal = true;
  245. window.ShowDialog();
  246. return DialogResult;
  247. }
  248. public DialogResult ShowDialog(IWin32Window owner)
  249. {
  250. Owner = owner as Form;
  251. return ShowDialog();
  252. }
  253. public void Close() => window.Close();
  254. public new void Hide() => window.Hide();
  255. protected virtual void OnDpiChanged(object sender, EventArgs e)
  256. {
  257. }
  258. private void EnumControls(Control control, Action<Control> action)
  259. {
  260. action(control);
  261. foreach (Control c in control.Controls)
  262. EnumControls(c, action);
  263. }
  264. public void MoveWindow(Rectangle rect) => MoveWindow(rect.Left, rect.Top, rect.Width, rect.Height);
  265. public void MoveWindow(int left, int top, int width, int height)
  266. {
  267. NativeMethods.MoveWindow(this, new Rectangle(left, top, width, height));
  268. Width = (int)(window.Width * DpiScale);
  269. Height = (int)(window.Height * DpiScale);
  270. }
  271. // this method is called by the FR dialog form designer
  272. public void MoveWindow(int left, int top, System.Windows.Media.Visual target)
  273. {
  274. // do default dpi scale
  275. SuspendLayout();
  276. ResumeLayout(false);
  277. // Suspend/Resume pair: fix issue with FR dialog form designer when moved to another monitor (bad anchors)
  278. SuspendLayout();
  279. double rescale = Helper.GetDpiScale(target) / DpiScale;
  280. NativeMethods.MoveWindow(this, new Rectangle(left, top, (int)(Width * rescale), (int)(Height * rescale)));
  281. AutoScaleDimensions = new SizeF((int)(DeviceDpi * rescale), (int)(DeviceDpi * rescale));
  282. // this.ResumeLayout will rescale controls, we don't need it
  283. base.ResumeLayout(false);
  284. // fix repaint issue
  285. EnumControls(this, c => { if (c != this) c.AfterDpiChange(1); });
  286. }
  287. public Form()
  288. {
  289. window = UseCustomWindowChrome ? new CustomControls.ChromeWindow() : new SystemWindow();
  290. var contentControl = ((IForm)window).ContentControl;
  291. SetContentControl(window, contentControl);
  292. contentControl.SizeChanged += (s, e) => UpdateLayout();
  293. window.UseLayoutRounding = true;
  294. #if Demo
  295. var demoLabel = new Controls.TextBlock();
  296. demoLabel.Text = " Demo version ";
  297. demoLabel.HorizontalAlignment = Windows.HorizontalAlignment.Center;
  298. demoLabel.VerticalAlignment = VerticalAlignment.Bottom;
  299. demoLabel.Opacity = 0.5;
  300. container.Children.Add(demoLabel);
  301. #endif
  302. window.Loaded += (sender, e) => OnShown(e); // this is the SWF Shown equivalent
  303. window.Closing += (sender, e) =>
  304. {
  305. isClosing = true;
  306. var args = new FormClosingEventArgs(CloseReason.UserClosing, false);
  307. OnFormClosing(args);
  308. e.Cancel = args.Cancel;
  309. if (args.Cancel)
  310. DialogResult = DialogResult.None;
  311. isClosing = false;
  312. };
  313. window.Closed += (sender, e) => OnFormClosed(new FormClosedEventArgs(CloseReason.UserClosing));
  314. window.StateChanged += (s, e) =>
  315. {
  316. if (window.WindowState == System.Windows.WindowState.Minimized && isModal && !ShowInTaskbar)
  317. {
  318. System.Windows.Application.Current.MainWindow.WindowState = System.Windows.WindowState.Minimized;
  319. }
  320. };
  321. DpiRescaler.Install(window, this, container, OnDpiChanged);
  322. AutoScaleDimensions = new System.Drawing.SizeF(96, 96);
  323. BackColor = System.Drawing.SystemColors.Control;
  324. }
  325. }
  326. }