Control.cs 37 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048
  1. using System.ComponentModel;
  2. using System.Drawing;
  3. using System.Windows.Media;
  4. using System.Windows.Media.Imaging;
  5. namespace System.Windows.Forms
  6. {
  7. public partial class Control : IComponent, IDisposable
  8. {
  9. private bool isDisposed;
  10. private Bitmap bitmapBuffer;
  11. private WriteableBitmap writeableBitmap;
  12. private bool isPainting;
  13. private bool updatingControlSize;
  14. private bool isMouseDblClicked;
  15. protected ControlStyles controlStyle;
  16. internal LayoutEngine LayoutEngine { get; }
  17. [Browsable(false)]
  18. public System.Windows.Controls.Control control { get; private set; }
  19. public string Name { get; set; }
  20. [Browsable(false)]
  21. public ControlCollection Controls { get; private set; }
  22. private Control parent;
  23. [Browsable(false)]
  24. public Control Parent
  25. {
  26. get => parent;
  27. set
  28. {
  29. if (parent != value)
  30. {
  31. if (parent != null)
  32. parent.Controls.Remove(this);
  33. if (value != null)
  34. value.Controls.Add(this);
  35. SetParent(value);
  36. }
  37. }
  38. }
  39. public bool Enabled
  40. {
  41. get => control.IsEnabled;
  42. set => control.IsEnabled = value;
  43. }
  44. public virtual bool Visible
  45. {
  46. get => control.Visibility == System.Windows.Visibility.Visible;
  47. set
  48. {
  49. control.Visibility = value ? System.Windows.Visibility.Visible : System.Windows.Visibility.Hidden; // Collapsed has issues with pmV2 (designer's preview, chart editor, etc)
  50. if (Dock != DockStyle.None)
  51. Parent?.UpdateLayout();
  52. }
  53. }
  54. [Browsable(false)]
  55. public bool Focused => control.IsFocused;
  56. public virtual bool AutoSize { get; set; }
  57. [Browsable(false)]
  58. public Padding Margin { get; set; } // ignored
  59. public virtual Padding Padding
  60. {
  61. get => Helper.ThicknessToPadding(control.Padding);
  62. set => control.Padding = Helper.PaddingToThickness(value);
  63. }
  64. internal Padding DefaultPadding { get; set; } // used in GroupBox to compensate for its header
  65. public AnchorStyles Anchor { get; set; }
  66. private DockStyle dock;
  67. public virtual DockStyle Dock
  68. {
  69. get => dock;
  70. set
  71. {
  72. if (dock != value)
  73. {
  74. dock = value;
  75. Parent?.UpdateLayout();
  76. }
  77. }
  78. }
  79. public virtual int Left
  80. {
  81. get => (int)Math.Round(control.Margin.Left * DpiScale);
  82. set
  83. {
  84. if (value != Left)
  85. {
  86. SetControlLeft(value);
  87. OnLocationChanged(EventArgs.Empty);
  88. }
  89. }
  90. }
  91. public virtual int Top
  92. {
  93. get => (int)Math.Round(control.Margin.Top * DpiScale);
  94. set
  95. {
  96. if (value != Top)
  97. {
  98. SetControlTop(value);
  99. OnLocationChanged(EventArgs.Empty);
  100. }
  101. }
  102. }
  103. private int height;
  104. public virtual int Height
  105. {
  106. get => AutoSize ? AutoSizeValue.Height : height;
  107. set
  108. {
  109. if (!AutoSize && value != height)
  110. {
  111. UpdateLayout(0, value - height);
  112. height = value;
  113. if (value >= 0 && !updatingControlSize)
  114. SetControlHeight(value);
  115. if (Dock != DockStyle.None)
  116. Parent?.UpdateLayout();
  117. OnResize(EventArgs.Empty);
  118. }
  119. else
  120. {
  121. // fix issue with FR LabelControl (setting size while AutoSize is on)
  122. height = value;
  123. }
  124. }
  125. }
  126. private int width;
  127. public virtual int Width
  128. {
  129. get => AutoSize ? AutoSizeValue.Width : width;
  130. set
  131. {
  132. if (!AutoSize && value != width)
  133. {
  134. UpdateLayout(value - width, 0);
  135. width = value;
  136. if (value >= 0 && !updatingControlSize)
  137. SetControlWidth(value);
  138. if (Dock != DockStyle.None)
  139. Parent?.UpdateLayout();
  140. OnResize(EventArgs.Empty);
  141. }
  142. else
  143. {
  144. // fix issue with FR LabelControl (setting size while AutoSize is on)
  145. width = value;
  146. }
  147. }
  148. }
  149. [Browsable(false)]
  150. public int Right => Left + Width;
  151. [Browsable(false)]
  152. public int Bottom => Top + Height;
  153. public System.Drawing.Point Location
  154. {
  155. get => new System.Drawing.Point(Left, Top);
  156. set
  157. {
  158. Left = value.X;
  159. Top = value.Y;
  160. }
  161. }
  162. public System.Drawing.Size Size
  163. {
  164. get => new System.Drawing.Size(Width, Height);
  165. set
  166. {
  167. Width = value.Width;
  168. Height = value.Height;
  169. }
  170. }
  171. [Browsable(false)]
  172. public Rectangle Bounds
  173. {
  174. get => new Rectangle(Left, Top, Width, Height);
  175. set
  176. {
  177. Location = value.Location;
  178. Size = value.Size;
  179. }
  180. }
  181. [Browsable(false)]
  182. public virtual Rectangle DisplayRectangle => new Rectangle(0, 0, Width, Height);
  183. [Browsable(false)]
  184. public virtual Rectangle ClientRectangle => DisplayRectangle;
  185. [Browsable(false)]
  186. public virtual System.Drawing.Size ClientSize
  187. {
  188. get => Size;
  189. set => Size = value;
  190. }
  191. private System.Drawing.Size minimumSize;
  192. public System.Drawing.Size MinimumSize
  193. {
  194. get => minimumSize;
  195. set
  196. {
  197. minimumSize = value;
  198. control.MinHeight = value.Height / DpiScale;
  199. control.MinWidth = value.Width / DpiScale;
  200. }
  201. }
  202. private System.Drawing.Size maximumSize;
  203. public System.Drawing.Size MaximumSize
  204. {
  205. get => maximumSize;
  206. set
  207. {
  208. maximumSize = value;
  209. control.MaxHeight = value.Height / DpiScale;
  210. control.MaxWidth = value.Width / DpiScale;
  211. }
  212. }
  213. [Browsable(false)]
  214. public virtual System.Drawing.Size PreferredSize => new System.Drawing.Size(Width, Height);
  215. [Browsable(false)]
  216. public int PreferredHeight => PreferredSize.Height;
  217. [Browsable(false)]
  218. public int PreferredWidth => PreferredSize.Width;
  219. public virtual System.Drawing.Color BackColor
  220. {
  221. get => Helper.GetColor(control.Background);
  222. set
  223. {
  224. control.Background = Helper.GetBrush(value);
  225. OnBackColorChanged(EventArgs.Empty);
  226. Refresh();
  227. }
  228. }
  229. public virtual System.Drawing.Color ForeColor
  230. {
  231. get => Helper.GetColor(control.Foreground);
  232. set => control.Foreground = Helper.GetBrush(value);
  233. }
  234. public virtual Image BackgroundImage { get; set; } // TODO?
  235. public virtual ImageLayout BackgroundImageLayout { get; set; } // TODO?
  236. public Font Font
  237. {
  238. get => Helper.GetFont(control);
  239. set
  240. {
  241. Helper.SetFont(control, value);
  242. ResetAutoSizeValue(false);
  243. OnFontChanged(EventArgs.Empty);
  244. }
  245. }
  246. public virtual BorderStyle BorderStyle
  247. {
  248. get => control.BorderThickness.Left == 0 ? BorderStyle.None : BorderStyle.FixedSingle;
  249. set => control.BorderThickness = new System.Windows.Thickness(value == BorderStyle.None ? 0 : 1);
  250. }
  251. private Cursor cursor = Cursors.Default;
  252. public Cursor Cursor
  253. {
  254. get => cursor;
  255. set
  256. {
  257. cursor = value;
  258. // reset global cursor
  259. Cursor.Current = null;
  260. control.Cursor = value.cursor;
  261. OnCursorChanged(EventArgs.Empty);
  262. }
  263. }
  264. private string text;
  265. public virtual string Text
  266. {
  267. get => text;
  268. set
  269. {
  270. text = value;
  271. ResetAutoSizeValue(false);
  272. }
  273. }
  274. public bool AllowDrop
  275. {
  276. get => control.AllowDrop;
  277. set => control.AllowDrop = value;
  278. }
  279. public int TabIndex
  280. {
  281. get => control.TabIndex == int.MaxValue ? 0 : control.TabIndex;
  282. set => control.TabIndex = value;
  283. }
  284. public bool TabStop
  285. {
  286. get => control.IsTabStop;
  287. set => control.IsTabStop = value;
  288. }
  289. public RightToLeft RightToLeft
  290. {
  291. get
  292. {
  293. return control.ReadLocalValue(FrameworkElement.FlowDirectionProperty) == DependencyProperty.UnsetValue ?
  294. RightToLeft.Inherit :
  295. (control.FlowDirection == FlowDirection.LeftToRight ? RightToLeft.No : RightToLeft.Yes);
  296. }
  297. set
  298. {
  299. if (value != RightToLeft)
  300. {
  301. if (value == RightToLeft.No)
  302. control.FlowDirection = FlowDirection.LeftToRight;
  303. else if (value == RightToLeft.Yes)
  304. control.FlowDirection = FlowDirection.RightToLeft;
  305. else
  306. control.ClearValue(FrameworkElement.FlowDirectionProperty);
  307. OnRightToLeftChanged();
  308. }
  309. }
  310. }
  311. public ContextMenuStrip ContextMenuStrip { get; set; }
  312. public string AccessibleName { get; set; }
  313. public AccessibleRole AccessibleRole { get; set; }
  314. public object Tag { get; set; }
  315. [Browsable(false)]
  316. public IntPtr Handle => IntPtr.Zero;
  317. [Browsable(false)]
  318. public ISite Site { get; set; }
  319. public ImeMode ImeMode { get; set; }
  320. [Browsable(false)]
  321. public int DeviceDpi => (int)(DpiScale * 96);
  322. [Browsable(false)]
  323. public double DpiScale => Helper.GetDpiScale(control);
  324. internal float _dpi(float value) => (float)(value * DpiScale);
  325. internal int _dpi(int value) => (int)(value * DpiScale);
  326. [Browsable(false)]
  327. public bool IsDisposed => isDisposed;
  328. public static Keys ModifierKeys => Helper.GetKeyModifiers(Input.Keyboard.Modifiers);
  329. public event EventHandler GotFocus;
  330. public event EventHandler Enter;
  331. public event EventHandler LostFocus;
  332. public event EventHandler Leave;
  333. public event MouseEventHandler MouseDown;
  334. public event MouseEventHandler MouseMove;
  335. public event MouseEventHandler MouseUp;
  336. public event MouseEventHandler MouseWheel;
  337. public event EventHandler MouseEnter;
  338. public event EventHandler MouseLeave;
  339. public event EventHandler MouseHover; // TODO?
  340. public event EventHandler Click;
  341. public event MouseEventHandler MouseClick;
  342. public event MouseEventHandler MouseDoubleClick;
  343. public event EventHandler DoubleClick;
  344. public event DragEventHandler DragEnter;
  345. public event EventHandler DragLeave;
  346. public event DragEventHandler DragOver;
  347. public event DragEventHandler DragDrop;
  348. public event KeyEventHandler KeyDown;
  349. public event KeyEventHandler KeyUp;
  350. public event KeyPressEventHandler KeyPress;
  351. public event PreviewKeyDownEventHandler PreviewKeyDown;
  352. public event PaintEventHandler Paint;
  353. public event EventHandler LocationChanged;
  354. public event EventHandler SizeChanged;
  355. public event EventHandler Resize;
  356. public event EventHandler BackColorChanged;
  357. public event EventHandler CursorChanged;
  358. public event EventHandler FontChanged;
  359. public event InvalidateEventHandler Invalidated;
  360. public event EventHandler RightToLeftChanged;
  361. public event EventHandler TextChanged;
  362. public event EventHandler EnabledChanged;
  363. public event EventHandler VisibleChanged;
  364. public event ControlEventHandler ControlAdded;
  365. public event ControlEventHandler ControlRemoved;
  366. public event EventHandler Disposed;
  367. private void WireEvents()
  368. {
  369. control.SizeChanged += Size_Changed;
  370. control.IsEnabledChanged += (sender, e) =>
  371. {
  372. OnEnabledChanged(EventArgs.Empty);
  373. Refresh();
  374. };
  375. control.IsVisibleChanged += (sender, e) =>
  376. {
  377. OnVisibleChanged(EventArgs.Empty);
  378. Refresh();
  379. };
  380. control.GotKeyboardFocus += (sender, e) =>
  381. {
  382. // commented: OnGotFocus is not called in PG textbox
  383. //if (!control.IsKeyboardFocusWithin)
  384. OnGotFocus(e);
  385. };
  386. control.LostFocus += (sender, e) =>
  387. {
  388. //if (!control.IsKeyboardFocusWithin)
  389. OnLostFocus(e);
  390. };
  391. control.MouseDown += (sender, e) =>
  392. {
  393. control.CaptureMouse();
  394. e.Handled = true;
  395. var args = Helper.GetMouseEventArgs(control, e);
  396. OnMouseDown(args);
  397. if (control.Focusable)
  398. control.Focus();
  399. // MouseDoubleClick event in WPF goes down to the root element regardless of e.Handled flag.
  400. // That's why we handle it here. Note that some controls such as TextBox does not work with this and use its own logic to fire OnDoubleClick.
  401. if (e.ClickCount == 2)
  402. isMouseDblClicked = true;
  403. if (e.ClickCount == 1)
  404. OnMouseClick(args);
  405. };
  406. control.MouseMove += (sender, e) =>
  407. {
  408. e.Handled = true;
  409. OnMouseMove(Helper.GetMouseEventArgs(control, e));
  410. };
  411. control.MouseUp += (sender, e) =>
  412. {
  413. e.Handled = true;
  414. var args = Helper.GetMouseEventArgs(control, e);
  415. if (isMouseDblClicked)
  416. {
  417. OnMouseDoubleClick(args);
  418. OnDoubleClick(EventArgs.Empty);
  419. isMouseDblClicked = false;
  420. }
  421. OnMouseUp(args);
  422. control.ReleaseMouseCapture();
  423. // SWF consistency - "ghost" mousemove event after mouseup (FR designer issue with TableObject's row/column context menu)
  424. OnMouseMove(new MouseEventArgs(MouseButtons.None, 0, args.X, args.Y, 0));
  425. if (e.ChangedButton == Input.MouseButton.Right)
  426. OnContextMenu(args);
  427. };
  428. control.MouseLeftButtonUp += (sender, e) => OnClick(e);
  429. control.MouseWheel += (sender, e) => OnMouseWheel(Helper.GetMouseEventArgs(control, e));
  430. control.MouseEnter += (sender, e) => OnMouseEnter(e); // note that these two events do not fire properly with parent/child controls
  431. control.MouseLeave += (sender, e) => OnMouseLeave(e); //
  432. control.DragEnter += (sender, e) => OnDragEnter(Helper.GetDragEventArgs(control, e));
  433. control.DragLeave += (sender, e) => OnDragLeave(Helper.GetDragEventArgs(control, e));
  434. control.DragOver += (sender, e) =>
  435. {
  436. var args = Helper.GetDragEventArgs(control, e);
  437. args.Effect = DragDropEffects.None;
  438. OnDragOver(args);
  439. e.Effects = (Windows.DragDropEffects)args.Effect;
  440. e.Handled = true;
  441. };
  442. control.Drop += (sender, e) => OnDragDrop(Helper.GetDragEventArgs(control, e));
  443. control.KeyDown += (sender, e) =>
  444. {
  445. // - do not handle keys pressed in the TextBox outside of TextBox. Case: keys typed in the inplace edit of FR table cell swallowed in the table's KeyDown handler
  446. // - keep in mind how TextBox is built here (ContentControl + either TextBox or PasswordBox)
  447. // - also note the Tag check: we need to handle events coming from internal textbox of some controls such as ComboBox (case: FR FontSizeComboBox, handle Enter key)
  448. if (e.OriginalSource is System.Windows.Controls.TextBox tb && tb.Tag != null && tb.Tag != this)
  449. return;
  450. var args = Helper.GetKeyEventArgs(e);
  451. OnKeyDown(args);
  452. if (IsInputKey(args.KeyData))
  453. args.Handled = true;
  454. e.Handled = args.Handled;
  455. };
  456. control.KeyUp += (sender, e) =>
  457. {
  458. var args = Helper.GetKeyEventArgs(e);
  459. OnKeyUp(args);
  460. e.Handled = args.Handled;
  461. };
  462. control.PreviewKeyDown += (sender, e) =>
  463. {
  464. var args = Helper.GetKeyEventArgs(e);
  465. if (ProcessDialogKey(args.KeyData))
  466. e.Handled = true;
  467. //var args1 = new PreviewKeyDownEventArgs(args.KeyData);
  468. //OnPreviewKeyDown(args1);
  469. //if (args1.IsInputKey)
  470. //e.Handled = true;
  471. };
  472. control.PreviewTextInput += (sender, e) =>
  473. {
  474. if (!string.IsNullOrEmpty(e.Text))
  475. {
  476. var args = new KeyPressEventArgs(e.Text[0]);
  477. OnKeyPress(args);
  478. e.Handled = args.Handled;
  479. }
  480. };
  481. }
  482. private void OnContextMenu(MouseEventArgs e)
  483. {
  484. if (ContextMenuStrip != null)
  485. ContextMenuStrip.Show(this, e.Location);
  486. }
  487. // these 4 are used in the TabItem control because it needs special handling
  488. protected virtual void SetControlLeft(int value) =>
  489. control.Margin = new System.Windows.Thickness(value / DpiScale, control.Margin.Top, control.Margin.Right, control.Margin.Bottom);
  490. protected virtual void SetControlTop(int value) =>
  491. control.Margin = new System.Windows.Thickness(control.Margin.Left, value / DpiScale, control.Margin.Right, control.Margin.Bottom);
  492. protected virtual void SetControlWidth(int value) =>
  493. control.Width = value / DpiScale;
  494. protected virtual void SetControlHeight(int value) =>
  495. control.Height = value / DpiScale;
  496. protected virtual void SetParent(Control parent) => this.parent = parent;
  497. // override in controls that don't use Margin to position itself (such as ToolStripItem)
  498. internal protected virtual SizeF GetControlDesiredSize()
  499. {
  500. // DesiredSize includes margin
  501. // if margin is negative and control is outside visible area, DesiredSize returns zero (case: FR dialog page controls when inserting a new label or checkbox)
  502. if (control.DesiredSize.Width == 0 || control.DesiredSize.Height == 0)
  503. return SizeF.Empty;
  504. return new SizeF((float)(control.DesiredSize.Width - control.Margin.Left), (float)(control.DesiredSize.Height - control.Margin.Top));
  505. }
  506. protected internal void Size_Changed(object sender, System.Windows.SizeChangedEventArgs e)
  507. {
  508. updatingControlSize = true; // needed to correctly restore maximized window
  509. if (e.WidthChanged)
  510. Width = (int)(e.NewSize.Width * DpiScale);
  511. if (e.HeightChanged)
  512. Height = (int)(e.NewSize.Height * DpiScale);
  513. updatingControlSize = false;
  514. }
  515. private void OnRightToLeftChanged()
  516. {
  517. OnRightToLeftChanged(EventArgs.Empty);
  518. ResetAutoSizeValue(AutoSize);
  519. foreach (Control c in Controls)
  520. {
  521. c.RightToLeft = RightToLeft;
  522. c.OnParentRightToLeftChanged(EventArgs.Empty);
  523. }
  524. }
  525. protected bool DesignMode => false;
  526. private System.Drawing.Size autoSizeValue;
  527. internal System.Drawing.Size AutoSizeValue
  528. {
  529. get
  530. {
  531. if (autoSizeValue.IsEmpty)
  532. {
  533. // commented out due to problems with button's GrowOnly
  534. // uncommented due to problems with button's GrowAndShrink
  535. control.Width = double.NaN;
  536. control.Height = double.NaN;
  537. control.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
  538. SizeF desiredSize = GetControlDesiredSize();
  539. autoSizeValue.Width = (int)Math.Round(desiredSize.Width * DpiScale);
  540. autoSizeValue.Height = (int)Math.Round(desiredSize.Height * DpiScale);
  541. }
  542. return autoSizeValue;
  543. }
  544. }
  545. internal void ResetAutoSizeValue(bool hardReset)
  546. {
  547. if (hardReset)
  548. control.Measure(new Size(10000, 10000));
  549. autoSizeValue = System.Drawing.Size.Empty;
  550. }
  551. internal virtual void AddChild(Control child) { }
  552. internal virtual void RemoveChild(Control child) { }
  553. internal virtual void SetChildIndex(Control child, int index) { }
  554. internal void DoControlAdded(Control c) => OnControlAdded(new ControlEventArgs(c));
  555. internal void DoControlRemoved(Control c) => OnControlRemoved(new ControlEventArgs(c));
  556. internal void UpdateLayout(int dx, int dy) => LayoutEngine.AddTransaction(dx, dy);
  557. internal void UpdateLayout() => LayoutEngine.AddTransaction();
  558. internal void OnLayout() => OnLayout(new LayoutEventArgs((IComponent)null, ""));
  559. private bool IsPaintingRequired()
  560. {
  561. // check if we have child controls with Dock = Fill that obscure this control. Take Padding into account
  562. if (Padding == Padding.Empty)
  563. {
  564. foreach (Control c in Controls)
  565. if (c.Dock == DockStyle.Fill)
  566. return false;
  567. }
  568. return true;
  569. }
  570. internal void DoPaint(System.Windows.Media.DrawingContext drawingContext)
  571. {
  572. if (Width <= 0 || Height <= 0 || !control.IsVisible)
  573. return;
  574. if (!IsPaintingRequired())
  575. return;
  576. isPainting = true;
  577. try
  578. {
  579. if (bitmapBuffer == null || bitmapBuffer.Width != Width || bitmapBuffer.Height != Height)
  580. {
  581. writeableBitmap = new WriteableBitmap(Width, Height, Helper.ScreenDpi, Helper.ScreenDpi, PixelFormats.Bgra32, null);
  582. bitmapBuffer?.Dispose();
  583. // share pixel buffer between gdi+ and wpf bitmap
  584. bitmapBuffer = new Bitmap(Width, Height, writeableBitmap.BackBufferStride, Drawing.Imaging.PixelFormat.Format32bppArgb, writeableBitmap.BackBuffer);
  585. }
  586. writeableBitmap.Lock();
  587. using (var g = Graphics.FromImage(bitmapBuffer))
  588. {
  589. var args = new PaintEventArgs(g, new Rectangle(0, 0, bitmapBuffer.Width, bitmapBuffer.Height));
  590. OnPaintBackground(args);
  591. OnPaint(args);
  592. }
  593. writeableBitmap.AddDirtyRect(new Int32Rect(0, 0, Width, Height));
  594. writeableBitmap.Unlock();
  595. if (control.FlowDirection == FlowDirection.RightToLeft)
  596. drawingContext.PushTransform(new MatrixTransform(-1, 0, 0, 1, Width / DpiScale, 0));
  597. drawingContext.DrawImage(writeableBitmap, new Rect(0, 0, Width / DpiScale, Height / DpiScale));
  598. }
  599. catch
  600. {
  601. drawingContext.DrawRectangle(Media.Brushes.White, null, new Rect(0, 0, Width, Height));
  602. drawingContext.DrawLine(new Media.Pen(Media.Brushes.Red, 1), new Point(0, 0), new Point(Width, Height));
  603. drawingContext.DrawLine(new Media.Pen(Media.Brushes.Red, 1), new Point(Width, 0), new Point(0, Height));
  604. }
  605. isPainting = false;
  606. }
  607. protected virtual void OnPaintBackground(PaintEventArgs e)
  608. {
  609. var color = BackColor;
  610. if (color == System.Drawing.Color.Transparent || (controlStyle & ControlStyles.SupportsTransparentBackColor) != 0)
  611. {
  612. // find parent control with non-transparent background
  613. var parent = Parent;
  614. while (parent != null)
  615. {
  616. if (parent.BackColor != System.Drawing.Color.Transparent)
  617. {
  618. color = parent.BackColor;
  619. break;
  620. }
  621. parent = parent.Parent;
  622. }
  623. }
  624. e.Graphics.Clear(color);
  625. }
  626. protected virtual void OnPaint(PaintEventArgs e) => Paint?.Invoke(this, e);
  627. protected virtual void OnGotFocus(EventArgs e)
  628. {
  629. GotFocus?.Invoke(this, e);
  630. Enter?.Invoke(this, e);
  631. }
  632. protected virtual void OnLostFocus(EventArgs e)
  633. {
  634. LostFocus?.Invoke(this, e);
  635. Leave?.Invoke(this, e);
  636. }
  637. protected virtual void OnMouseDown(MouseEventArgs e) => MouseDown?.Invoke(this, e);
  638. protected virtual void OnMouseMove(MouseEventArgs e) => MouseMove?.Invoke(this, e);
  639. protected virtual void OnMouseUp(MouseEventArgs e) => MouseUp?.Invoke(this, e);
  640. protected virtual void OnMouseWheel(MouseEventArgs e) => MouseWheel?.Invoke(this, e);
  641. protected virtual void OnMouseEnter(EventArgs e) => MouseEnter?.Invoke(this, e);
  642. protected virtual void OnMouseLeave(EventArgs e) => MouseLeave?.Invoke(this, e);
  643. protected virtual void OnClick(EventArgs e) => Click?.Invoke(this, e);
  644. protected virtual void OnDoubleClick(EventArgs e) => DoubleClick?.Invoke(this, e);
  645. protected virtual void OnMouseClick(MouseEventArgs e) => MouseClick?.Invoke(this, e);
  646. protected virtual void OnMouseDoubleClick(MouseEventArgs e) => MouseDoubleClick?.Invoke(this, e);
  647. protected virtual void OnDragEnter(DragEventArgs e) => DragEnter?.Invoke(this, e);
  648. protected virtual void OnDragLeave(EventArgs e) => DragLeave?.Invoke(this, e);
  649. protected virtual void OnDragOver(DragEventArgs e) => DragOver?.Invoke(this, e);
  650. protected virtual void OnDragDrop(DragEventArgs e) => DragDrop?.Invoke(this, e);
  651. protected virtual void OnKeyDown(KeyEventArgs e) => KeyDown?.Invoke(this, e);
  652. protected virtual void OnKeyUp(KeyEventArgs e) => KeyUp?.Invoke(this, e);
  653. protected virtual void OnKeyPress(KeyPressEventArgs e) => KeyPress?.Invoke(this, e);
  654. protected virtual void OnPreviewKeyDown(PreviewKeyDownEventArgs e) => PreviewKeyDown?.Invoke(this, e);
  655. protected virtual void OnLayout(LayoutEventArgs e) { }
  656. protected virtual void OnResize(EventArgs e)
  657. {
  658. OnSizeChanged(e);
  659. Resize?.Invoke(this, e);
  660. }
  661. protected virtual void OnLocationChanged(EventArgs e) => LocationChanged?.Invoke(this, e);
  662. protected virtual void OnSizeChanged(EventArgs e) => SizeChanged?.Invoke(this, e);
  663. protected virtual void OnInvalidated(InvalidateEventArgs e) => Invalidated?.Invoke(this, e);
  664. protected virtual void OnBackColorChanged(EventArgs e) => BackColorChanged?.Invoke(this, e);
  665. protected virtual void OnSystemColorsChanged(EventArgs e) { } // TODO?
  666. protected virtual void OnCursorChanged(EventArgs e) => CursorChanged?.Invoke(this, e);
  667. protected virtual void OnFontChanged(EventArgs e) => FontChanged?.Invoke(this, e);
  668. protected virtual void OnRightToLeftChanged(EventArgs e) => RightToLeftChanged?.Invoke(this, e);
  669. protected virtual void OnParentRightToLeftChanged(EventArgs e) { }
  670. protected virtual void OnTextChanged(EventArgs e) => TextChanged?.Invoke(this, e);
  671. protected virtual void OnEnabledChanged(EventArgs e) => EnabledChanged?.Invoke(this, e);
  672. protected virtual void OnVisibleChanged(EventArgs e) => VisibleChanged?.Invoke(this, e);
  673. protected virtual void OnControlAdded(ControlEventArgs e) => ControlAdded?.Invoke(this, e);
  674. protected virtual void OnControlRemoved(ControlEventArgs e) => ControlRemoved?.Invoke(this, e);
  675. protected virtual object GetService(object service) => null;
  676. protected virtual object GetService(Type serviceType) => null;
  677. protected void SetStyle(ControlStyles flag, bool value)
  678. {
  679. controlStyle = (value ? (controlStyle | flag) : (controlStyle & ~flag));
  680. // FR designer's smarttag button depends on this
  681. control.Focusable = (controlStyle & ControlStyles.Selectable) != 0;
  682. }
  683. protected virtual void ScaleCore(float dx, float dy)
  684. {
  685. if (control is not Window)
  686. Location = new System.Drawing.Point((int)(Location.X * dx), (int)(Location.Y * dy));
  687. if (!AutoSize)
  688. ClientSize = new System.Drawing.Size((int)(ClientSize.Width * dx), (int)(ClientSize.Height * dy));
  689. }
  690. protected virtual bool IsInputKey(Keys keyData) => false;
  691. protected virtual bool ProcessDialogKey(Keys keyData) => false;
  692. protected void SetControl(System.Windows.Controls.Control control)
  693. {
  694. this.control = control;
  695. control.Tag = this;
  696. control.HorizontalAlignment = System.Windows.HorizontalAlignment.Left;
  697. control.VerticalAlignment = System.Windows.VerticalAlignment.Top;
  698. Controls = new ControlCollection(this);
  699. WireEvents();
  700. AllowDrop = false;
  701. }
  702. protected virtual void Dispose(bool disposing)
  703. {
  704. if (!disposing)
  705. return;
  706. if (parent != null)
  707. parent.Controls.Remove(this);
  708. for (int i = 0; i < Controls.Count; i++)
  709. {
  710. Control control = Controls[i];
  711. control.parent = null;
  712. control.Dispose();
  713. }
  714. isDisposed = true;
  715. bitmapBuffer?.Dispose();
  716. bitmapBuffer = null;
  717. Disposed?.Invoke(this, EventArgs.Empty);
  718. }
  719. protected virtual System.Drawing.Size DefaultSize => new Drawing.Size(Width, Height);
  720. public void Dispose()
  721. {
  722. Dispose(true);
  723. }
  724. public void DoDragDrop(object data, DragDropEffects allowedEffects) =>
  725. System.Windows.DragDrop.DoDragDrop(control, data, (System.Windows.DragDropEffects)allowedEffects);
  726. // this is used in drag/drop operations which already return client coords, so do nothing
  727. public System.Drawing.Point PointToClient(System.Drawing.Point pos) => pos;
  728. public void Scale(float dx, float dy)
  729. {
  730. if (dx == 1 && dy == 1)
  731. return;
  732. SuspendLayout();
  733. ScaleCore(dx, dy);
  734. ResetAutoSizeValue(false);
  735. for (int i = 0; i < Controls.Count; i++)
  736. {
  737. Control c = Controls[i];
  738. c.Scale(dx, dy);
  739. }
  740. ResumeLayout(false);
  741. OnLayout();
  742. }
  743. private void EnumControls(Control control, Action<Control> action)
  744. {
  745. action(control);
  746. foreach (Control c in control.Controls)
  747. EnumControls(c, action);
  748. }
  749. public void Scale(SizeF scale)
  750. {
  751. if (this.DeviceDpi != Helper.ScreenDpi)
  752. {
  753. float sc = (float)Helper.ScreenDpi / 96;
  754. Scale(sc, sc);
  755. sc = scale.Width / sc;
  756. // we have to return correct width & height to the calling method
  757. width = (int)(width * sc);
  758. height = (int)(height * sc);
  759. // necessary for cases like 120->144 scale (case: FR connection form)
  760. int saveWidth = width;
  761. int saveHeight = height;
  762. bool layoutComplete = false;
  763. EnumControls(this, c => c.SuspendLayout());
  764. control.Dispatcher.InvokeAsync(() =>
  765. {
  766. Size = new Drawing.Size(saveWidth, saveHeight);
  767. EnumControls(this, c =>
  768. {
  769. if (c is ContainerControl ctl)
  770. ctl.ScaleInternals(sc, sc);
  771. c.ResumeLayout(false);
  772. c.Refresh();
  773. });
  774. layoutComplete = true;
  775. }, Threading.DispatcherPriority.Render);
  776. // return to the calling method when layout completed to make rtl working properly (case: connection form & rtl & pmV2)
  777. while (!layoutComplete)
  778. Application.DoEvents();
  779. return;
  780. }
  781. Scale(scale.Width, scale.Height);
  782. }
  783. internal void BeforeDpiChange()
  784. {
  785. SuspendLayout();
  786. }
  787. internal virtual void AfterDpiChange(float rescale)
  788. {
  789. if (!double.IsNaN(control.Width))
  790. width = (int)(control.Width * DpiScale);
  791. else
  792. width = (int)(control.ActualWidth * DpiScale);
  793. if (!double.IsNaN(control.Height))
  794. height = (int)(control.Height * DpiScale);
  795. else
  796. height = (int)(control.ActualHeight * DpiScale);
  797. ResetAutoSizeValue(false);
  798. ResumeLayout(false);
  799. OnLayout();
  800. }
  801. public void SetBounds(int left, int top, int width, int height) => Bounds = new Rectangle(left, top, width, height);
  802. public void Focus() => control.Focus();
  803. public void Show() => Visible = true;
  804. public void Hide() => Visible = false;
  805. public void BringToFront() => Parent?.Controls.SetChildIndex(this, 0);
  806. public void SendToBack() => Parent?.Controls.SetChildIndex(this, Parent.Controls.Count - 1);
  807. public virtual void Select() { }
  808. public virtual void Refresh()
  809. {
  810. if (isPainting)
  811. return;
  812. control.InvalidateVisual();
  813. }
  814. public void Invalidate()
  815. {
  816. Refresh();
  817. OnInvalidated(new InvalidateEventArgs(DisplayRectangle));
  818. }
  819. public void Invalidate(bool flag) => Invalidate();
  820. public void Invalidate(Rectangle rect) => Invalidate();
  821. public void Update() { }
  822. public Form FindForm() => System.Windows.Window.GetWindow(control)?.Tag as Form;
  823. public void SuspendLayout() => LayoutEngine.SuspendLayout();
  824. public virtual void ResumeLayout(bool resume)
  825. {
  826. LayoutEngine.ResumeLayout(resume);
  827. if (!resume)
  828. UpdateLayout(); // update docked controls (case: barcode editor form with tvData docked incorrectly)
  829. }
  830. public void ResumeLayout() => ResumeLayout(true);
  831. public void PerformLayout() { }
  832. public void CreateControl() { }
  833. public void ResetText() => Text = "";
  834. public Graphics CreateGraphics() => Graphics.FromImage(new Bitmap(1, 1)); // TODO: FIXME all use cases
  835. public void Invoke(Action action) => control.Dispatcher.Invoke(action);
  836. public virtual Drawing.Point PointToScreen(Drawing.Point point)
  837. {
  838. if (!control.IsLoaded)
  839. return point;
  840. var pt = control.PointToScreen(new Point(point.X / DpiScale, point.Y / DpiScale));
  841. // pt is already in screen physical coords, no need to dpi-scale it
  842. return new Drawing.Point((int)(pt.X), (int)(pt.Y));
  843. }
  844. public virtual void DrawToBitmap(Bitmap bitmap, Rectangle rect)
  845. {
  846. Helper.DrawControl(control, bitmap, Width, Height, DeviceDpi);
  847. }
  848. public Control()
  849. {
  850. LayoutEngine = new(this);
  851. Anchor = AnchorStyles.Left | AnchorStyles.Top;
  852. controlStyle = ControlStyles.Selectable;
  853. SetControl(new OwnerDrawControl(this));
  854. }
  855. ~Control()
  856. {
  857. Dispose(false);
  858. }
  859. }
  860. }