using System.ComponentModel; using System.Drawing; using System.Windows.Media; using System.Windows.Media.Imaging; namespace System.Windows.Forms { public partial class Control : IComponent, IDisposable { private bool isDisposed; private Bitmap bitmapBuffer; private WriteableBitmap writeableBitmap; private bool isPainting; private bool updatingControlSize; private bool isMouseDblClicked; protected ControlStyles controlStyle; internal LayoutEngine LayoutEngine { get; } [Browsable(false)] public System.Windows.Controls.Control control { get; private set; } public string Name { get; set; } [Browsable(false)] public ControlCollection Controls { get; private set; } private Control parent; [Browsable(false)] public Control Parent { get => parent; set { if (parent != value) { if (parent != null) parent.Controls.Remove(this); if (value != null) value.Controls.Add(this); SetParent(value); } } } public bool Enabled { get => control.IsEnabled; set => control.IsEnabled = value; } public virtual bool Visible { get => control.Visibility == System.Windows.Visibility.Visible; set { control.Visibility = value ? System.Windows.Visibility.Visible : System.Windows.Visibility.Hidden; // Collapsed has issues with pmV2 (designer's preview, chart editor, etc) if (Dock != DockStyle.None) Parent?.UpdateLayout(); } } [Browsable(false)] public bool Focused => control.IsFocused; public virtual bool AutoSize { get; set; } [Browsable(false)] public Padding Margin { get; set; } // ignored public virtual Padding Padding { get => Helper.ThicknessToPadding(control.Padding); set => control.Padding = Helper.PaddingToThickness(value); } internal Padding DefaultPadding { get; set; } // used in GroupBox to compensate for its header public AnchorStyles Anchor { get; set; } private DockStyle dock; public virtual DockStyle Dock { get => dock; set { if (dock != value) { dock = value; Parent?.UpdateLayout(); } } } public virtual int Left { get => (int)Math.Round(control.Margin.Left * DpiScale); set { if (value != Left) { SetControlLeft(value); OnLocationChanged(EventArgs.Empty); } } } public virtual int Top { get => (int)Math.Round(control.Margin.Top * DpiScale); set { if (value != Top) { SetControlTop(value); OnLocationChanged(EventArgs.Empty); } } } private int height; public virtual int Height { get => AutoSize ? AutoSizeValue.Height : height; set { if (!AutoSize && value != height) { UpdateLayout(0, value - height); height = value; if (value >= 0 && !updatingControlSize) SetControlHeight(value); if (Dock != DockStyle.None) Parent?.UpdateLayout(); OnResize(EventArgs.Empty); } else { // fix issue with FR LabelControl (setting size while AutoSize is on) height = value; } } } private int width; public virtual int Width { get => AutoSize ? AutoSizeValue.Width : width; set { if (!AutoSize && value != width) { UpdateLayout(value - width, 0); width = value; if (value >= 0 && !updatingControlSize) SetControlWidth(value); if (Dock != DockStyle.None) Parent?.UpdateLayout(); OnResize(EventArgs.Empty); } else { // fix issue with FR LabelControl (setting size while AutoSize is on) width = value; } } } [Browsable(false)] public int Right => Left + Width; [Browsable(false)] public int Bottom => Top + Height; public System.Drawing.Point Location { get => new System.Drawing.Point(Left, Top); set { Left = value.X; Top = value.Y; } } public System.Drawing.Size Size { get => new System.Drawing.Size(Width, Height); set { Width = value.Width; Height = value.Height; } } [Browsable(false)] public Rectangle Bounds { get => new Rectangle(Left, Top, Width, Height); set { Location = value.Location; Size = value.Size; } } [Browsable(false)] public virtual Rectangle DisplayRectangle => new Rectangle(0, 0, Width, Height); [Browsable(false)] public virtual Rectangle ClientRectangle => DisplayRectangle; [Browsable(false)] public virtual System.Drawing.Size ClientSize { get => Size; set => Size = value; } private System.Drawing.Size minimumSize; public System.Drawing.Size MinimumSize { get => minimumSize; set { minimumSize = value; control.MinHeight = value.Height / DpiScale; control.MinWidth = value.Width / DpiScale; } } private System.Drawing.Size maximumSize; public System.Drawing.Size MaximumSize { get => maximumSize; set { maximumSize = value; control.MaxHeight = value.Height / DpiScale; control.MaxWidth = value.Width / DpiScale; } } [Browsable(false)] public virtual System.Drawing.Size PreferredSize => new System.Drawing.Size(Width, Height); [Browsable(false)] public int PreferredHeight => PreferredSize.Height; [Browsable(false)] public int PreferredWidth => PreferredSize.Width; public virtual System.Drawing.Color BackColor { get => Helper.GetColor(control.Background); set { control.Background = Helper.GetBrush(value); OnBackColorChanged(EventArgs.Empty); Refresh(); } } public virtual System.Drawing.Color ForeColor { get => Helper.GetColor(control.Foreground); set => control.Foreground = Helper.GetBrush(value); } public virtual Image BackgroundImage { get; set; } // TODO? public virtual ImageLayout BackgroundImageLayout { get; set; } // TODO? public Font Font { get => Helper.GetFont(control); set { Helper.SetFont(control, value); ResetAutoSizeValue(false); OnFontChanged(EventArgs.Empty); } } public virtual BorderStyle BorderStyle { get => control.BorderThickness.Left == 0 ? BorderStyle.None : BorderStyle.FixedSingle; set => control.BorderThickness = new System.Windows.Thickness(value == BorderStyle.None ? 0 : 1); } private Cursor cursor = Cursors.Default; public Cursor Cursor { get => cursor; set { cursor = value; // reset global cursor Cursor.Current = null; control.Cursor = value.cursor; OnCursorChanged(EventArgs.Empty); } } private string text; public virtual string Text { get => text; set { text = value; ResetAutoSizeValue(false); } } public bool AllowDrop { get => control.AllowDrop; set => control.AllowDrop = value; } public int TabIndex { get => control.TabIndex == int.MaxValue ? 0 : control.TabIndex; set => control.TabIndex = value; } public bool TabStop { get => control.IsTabStop; set => control.IsTabStop = value; } public RightToLeft RightToLeft { get { return control.ReadLocalValue(FrameworkElement.FlowDirectionProperty) == DependencyProperty.UnsetValue ? RightToLeft.Inherit : (control.FlowDirection == FlowDirection.LeftToRight ? RightToLeft.No : RightToLeft.Yes); } set { if (value != RightToLeft) { if (value == RightToLeft.No) control.FlowDirection = FlowDirection.LeftToRight; else if (value == RightToLeft.Yes) control.FlowDirection = FlowDirection.RightToLeft; else control.ClearValue(FrameworkElement.FlowDirectionProperty); OnRightToLeftChanged(); } } } public ContextMenuStrip ContextMenuStrip { get; set; } public string AccessibleName { get; set; } public AccessibleRole AccessibleRole { get; set; } public object Tag { get; set; } [Browsable(false)] public IntPtr Handle => IntPtr.Zero; [Browsable(false)] public ISite Site { get; set; } public ImeMode ImeMode { get; set; } [Browsable(false)] public int DeviceDpi => (int)(DpiScale * 96); [Browsable(false)] public double DpiScale => Helper.GetDpiScale(control); internal float _dpi(float value) => (float)(value * DpiScale); internal int _dpi(int value) => (int)(value * DpiScale); [Browsable(false)] public bool IsDisposed => isDisposed; public static Keys ModifierKeys => Helper.GetKeyModifiers(Input.Keyboard.Modifiers); public event EventHandler GotFocus; public event EventHandler Enter; public event EventHandler LostFocus; public event EventHandler Leave; public event MouseEventHandler MouseDown; public event MouseEventHandler MouseMove; public event MouseEventHandler MouseUp; public event MouseEventHandler MouseWheel; public event EventHandler MouseEnter; public event EventHandler MouseLeave; public event EventHandler MouseHover; // TODO? public event EventHandler Click; public event MouseEventHandler MouseClick; public event MouseEventHandler MouseDoubleClick; public event EventHandler DoubleClick; public event DragEventHandler DragEnter; public event EventHandler DragLeave; public event DragEventHandler DragOver; public event DragEventHandler DragDrop; public event KeyEventHandler KeyDown; public event KeyEventHandler KeyUp; public event KeyPressEventHandler KeyPress; public event PreviewKeyDownEventHandler PreviewKeyDown; public event PaintEventHandler Paint; public event EventHandler LocationChanged; public event EventHandler SizeChanged; public event EventHandler Resize; public event EventHandler BackColorChanged; public event EventHandler CursorChanged; public event EventHandler FontChanged; public event InvalidateEventHandler Invalidated; public event EventHandler RightToLeftChanged; public event EventHandler TextChanged; public event EventHandler EnabledChanged; public event EventHandler VisibleChanged; public event ControlEventHandler ControlAdded; public event ControlEventHandler ControlRemoved; public event EventHandler Disposed; private void WireEvents() { control.SizeChanged += Size_Changed; control.IsEnabledChanged += (sender, e) => { OnEnabledChanged(EventArgs.Empty); Refresh(); }; control.IsVisibleChanged += (sender, e) => { OnVisibleChanged(EventArgs.Empty); Refresh(); }; control.GotKeyboardFocus += (sender, e) => { // commented: OnGotFocus is not called in PG textbox //if (!control.IsKeyboardFocusWithin) OnGotFocus(e); }; control.LostFocus += (sender, e) => { //if (!control.IsKeyboardFocusWithin) OnLostFocus(e); }; control.MouseDown += (sender, e) => { control.CaptureMouse(); e.Handled = true; var args = Helper.GetMouseEventArgs(control, e); OnMouseDown(args); if (control.Focusable) control.Focus(); // MouseDoubleClick event in WPF goes down to the root element regardless of e.Handled flag. // 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. if (e.ClickCount == 2) isMouseDblClicked = true; if (e.ClickCount == 1) OnMouseClick(args); }; control.MouseMove += (sender, e) => { e.Handled = true; OnMouseMove(Helper.GetMouseEventArgs(control, e)); }; control.MouseUp += (sender, e) => { e.Handled = true; var args = Helper.GetMouseEventArgs(control, e); if (isMouseDblClicked) { OnMouseDoubleClick(args); OnDoubleClick(EventArgs.Empty); isMouseDblClicked = false; } OnMouseUp(args); control.ReleaseMouseCapture(); // SWF consistency - "ghost" mousemove event after mouseup (FR designer issue with TableObject's row/column context menu) OnMouseMove(new MouseEventArgs(MouseButtons.None, 0, args.X, args.Y, 0)); if (e.ChangedButton == Input.MouseButton.Right) OnContextMenu(args); }; control.MouseLeftButtonUp += (sender, e) => OnClick(e); control.MouseWheel += (sender, e) => OnMouseWheel(Helper.GetMouseEventArgs(control, e)); control.MouseEnter += (sender, e) => OnMouseEnter(e); // note that these two events do not fire properly with parent/child controls control.MouseLeave += (sender, e) => OnMouseLeave(e); // control.DragEnter += (sender, e) => OnDragEnter(Helper.GetDragEventArgs(control, e)); control.DragLeave += (sender, e) => OnDragLeave(Helper.GetDragEventArgs(control, e)); control.DragOver += (sender, e) => { var args = Helper.GetDragEventArgs(control, e); args.Effect = DragDropEffects.None; OnDragOver(args); e.Effects = (Windows.DragDropEffects)args.Effect; e.Handled = true; }; control.Drop += (sender, e) => OnDragDrop(Helper.GetDragEventArgs(control, e)); control.KeyDown += (sender, e) => { // - 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 // - keep in mind how TextBox is built here (ContentControl + either TextBox or PasswordBox) // - 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) if (e.OriginalSource is System.Windows.Controls.TextBox tb && tb.Tag != null && tb.Tag != this) return; var args = Helper.GetKeyEventArgs(e); OnKeyDown(args); if (IsInputKey(args.KeyData)) args.Handled = true; e.Handled = args.Handled; }; control.KeyUp += (sender, e) => { var args = Helper.GetKeyEventArgs(e); OnKeyUp(args); e.Handled = args.Handled; }; control.PreviewKeyDown += (sender, e) => { var args = Helper.GetKeyEventArgs(e); if (ProcessDialogKey(args.KeyData)) e.Handled = true; //var args1 = new PreviewKeyDownEventArgs(args.KeyData); //OnPreviewKeyDown(args1); //if (args1.IsInputKey) //e.Handled = true; }; control.PreviewTextInput += (sender, e) => { if (!string.IsNullOrEmpty(e.Text)) { var args = new KeyPressEventArgs(e.Text[0]); OnKeyPress(args); e.Handled = args.Handled; } }; } private void OnContextMenu(MouseEventArgs e) { if (ContextMenuStrip != null) ContextMenuStrip.Show(this, e.Location); } // these 4 are used in the TabItem control because it needs special handling protected virtual void SetControlLeft(int value) => control.Margin = new System.Windows.Thickness(value / DpiScale, control.Margin.Top, control.Margin.Right, control.Margin.Bottom); protected virtual void SetControlTop(int value) => control.Margin = new System.Windows.Thickness(control.Margin.Left, value / DpiScale, control.Margin.Right, control.Margin.Bottom); protected virtual void SetControlWidth(int value) => control.Width = value / DpiScale; protected virtual void SetControlHeight(int value) => control.Height = value / DpiScale; protected virtual void SetParent(Control parent) => this.parent = parent; // override in controls that don't use Margin to position itself (such as ToolStripItem) internal protected virtual SizeF GetControlDesiredSize() { // DesiredSize includes margin // 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) if (control.DesiredSize.Width == 0 || control.DesiredSize.Height == 0) return SizeF.Empty; return new SizeF((float)(control.DesiredSize.Width - control.Margin.Left), (float)(control.DesiredSize.Height - control.Margin.Top)); } protected internal void Size_Changed(object sender, System.Windows.SizeChangedEventArgs e) { updatingControlSize = true; // needed to correctly restore maximized window if (e.WidthChanged) Width = (int)(e.NewSize.Width * DpiScale); if (e.HeightChanged) Height = (int)(e.NewSize.Height * DpiScale); updatingControlSize = false; } private void OnRightToLeftChanged() { OnRightToLeftChanged(EventArgs.Empty); ResetAutoSizeValue(AutoSize); foreach (Control c in Controls) { c.RightToLeft = RightToLeft; c.OnParentRightToLeftChanged(EventArgs.Empty); } } protected bool DesignMode => false; private System.Drawing.Size autoSizeValue; internal System.Drawing.Size AutoSizeValue { get { if (autoSizeValue.IsEmpty) { // commented out due to problems with button's GrowOnly // uncommented due to problems with button's GrowAndShrink control.Width = double.NaN; control.Height = double.NaN; control.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity)); SizeF desiredSize = GetControlDesiredSize(); autoSizeValue.Width = (int)Math.Round(desiredSize.Width * DpiScale); autoSizeValue.Height = (int)Math.Round(desiredSize.Height * DpiScale); } return autoSizeValue; } } internal void ResetAutoSizeValue(bool hardReset) { if (hardReset) control.Measure(new Size(10000, 10000)); autoSizeValue = System.Drawing.Size.Empty; } internal virtual void AddChild(Control child) { } internal virtual void RemoveChild(Control child) { } internal virtual void SetChildIndex(Control child, int index) { } internal void DoControlAdded(Control c) => OnControlAdded(new ControlEventArgs(c)); internal void DoControlRemoved(Control c) => OnControlRemoved(new ControlEventArgs(c)); internal void UpdateLayout(int dx, int dy) => LayoutEngine.AddTransaction(dx, dy); internal void UpdateLayout() => LayoutEngine.AddTransaction(); internal void OnLayout() => OnLayout(new LayoutEventArgs((IComponent)null, "")); private bool IsPaintingRequired() { // check if we have child controls with Dock = Fill that obscure this control. Take Padding into account if (Padding == Padding.Empty) { foreach (Control c in Controls) if (c.Dock == DockStyle.Fill) return false; } return true; } internal void DoPaint(System.Windows.Media.DrawingContext drawingContext) { if (Width <= 0 || Height <= 0 || !control.IsVisible) return; if (!IsPaintingRequired()) return; isPainting = true; try { if (bitmapBuffer == null || bitmapBuffer.Width != Width || bitmapBuffer.Height != Height) { writeableBitmap = new WriteableBitmap(Width, Height, Helper.ScreenDpi, Helper.ScreenDpi, PixelFormats.Bgra32, null); bitmapBuffer?.Dispose(); // share pixel buffer between gdi+ and wpf bitmap bitmapBuffer = new Bitmap(Width, Height, writeableBitmap.BackBufferStride, Drawing.Imaging.PixelFormat.Format32bppArgb, writeableBitmap.BackBuffer); } writeableBitmap.Lock(); using (var g = Graphics.FromImage(bitmapBuffer)) { var args = new PaintEventArgs(g, new Rectangle(0, 0, bitmapBuffer.Width, bitmapBuffer.Height)); OnPaintBackground(args); OnPaint(args); } writeableBitmap.AddDirtyRect(new Int32Rect(0, 0, Width, Height)); writeableBitmap.Unlock(); if (control.FlowDirection == FlowDirection.RightToLeft) drawingContext.PushTransform(new MatrixTransform(-1, 0, 0, 1, Width / DpiScale, 0)); drawingContext.DrawImage(writeableBitmap, new Rect(0, 0, Width / DpiScale, Height / DpiScale)); } catch { drawingContext.DrawRectangle(Media.Brushes.White, null, new Rect(0, 0, Width, Height)); drawingContext.DrawLine(new Media.Pen(Media.Brushes.Red, 1), new Point(0, 0), new Point(Width, Height)); drawingContext.DrawLine(new Media.Pen(Media.Brushes.Red, 1), new Point(Width, 0), new Point(0, Height)); } isPainting = false; } protected virtual void OnPaintBackground(PaintEventArgs e) { var color = BackColor; if (color == System.Drawing.Color.Transparent || (controlStyle & ControlStyles.SupportsTransparentBackColor) != 0) { // find parent control with non-transparent background var parent = Parent; while (parent != null) { if (parent.BackColor != System.Drawing.Color.Transparent) { color = parent.BackColor; break; } parent = parent.Parent; } } e.Graphics.Clear(color); } protected virtual void OnPaint(PaintEventArgs e) => Paint?.Invoke(this, e); protected virtual void OnGotFocus(EventArgs e) { GotFocus?.Invoke(this, e); Enter?.Invoke(this, e); } protected virtual void OnLostFocus(EventArgs e) { LostFocus?.Invoke(this, e); Leave?.Invoke(this, e); } protected virtual void OnMouseDown(MouseEventArgs e) => MouseDown?.Invoke(this, e); protected virtual void OnMouseMove(MouseEventArgs e) => MouseMove?.Invoke(this, e); protected virtual void OnMouseUp(MouseEventArgs e) => MouseUp?.Invoke(this, e); protected virtual void OnMouseWheel(MouseEventArgs e) => MouseWheel?.Invoke(this, e); protected virtual void OnMouseEnter(EventArgs e) => MouseEnter?.Invoke(this, e); protected virtual void OnMouseLeave(EventArgs e) => MouseLeave?.Invoke(this, e); protected virtual void OnClick(EventArgs e) => Click?.Invoke(this, e); protected virtual void OnDoubleClick(EventArgs e) => DoubleClick?.Invoke(this, e); protected virtual void OnMouseClick(MouseEventArgs e) => MouseClick?.Invoke(this, e); protected virtual void OnMouseDoubleClick(MouseEventArgs e) => MouseDoubleClick?.Invoke(this, e); protected virtual void OnDragEnter(DragEventArgs e) => DragEnter?.Invoke(this, e); protected virtual void OnDragLeave(EventArgs e) => DragLeave?.Invoke(this, e); protected virtual void OnDragOver(DragEventArgs e) => DragOver?.Invoke(this, e); protected virtual void OnDragDrop(DragEventArgs e) => DragDrop?.Invoke(this, e); protected virtual void OnKeyDown(KeyEventArgs e) => KeyDown?.Invoke(this, e); protected virtual void OnKeyUp(KeyEventArgs e) => KeyUp?.Invoke(this, e); protected virtual void OnKeyPress(KeyPressEventArgs e) => KeyPress?.Invoke(this, e); protected virtual void OnPreviewKeyDown(PreviewKeyDownEventArgs e) => PreviewKeyDown?.Invoke(this, e); protected virtual void OnLayout(LayoutEventArgs e) { } protected virtual void OnResize(EventArgs e) { OnSizeChanged(e); Resize?.Invoke(this, e); } protected virtual void OnLocationChanged(EventArgs e) => LocationChanged?.Invoke(this, e); protected virtual void OnSizeChanged(EventArgs e) => SizeChanged?.Invoke(this, e); protected virtual void OnInvalidated(InvalidateEventArgs e) => Invalidated?.Invoke(this, e); protected virtual void OnBackColorChanged(EventArgs e) => BackColorChanged?.Invoke(this, e); protected virtual void OnSystemColorsChanged(EventArgs e) { } // TODO? protected virtual void OnCursorChanged(EventArgs e) => CursorChanged?.Invoke(this, e); protected virtual void OnFontChanged(EventArgs e) => FontChanged?.Invoke(this, e); protected virtual void OnRightToLeftChanged(EventArgs e) => RightToLeftChanged?.Invoke(this, e); protected virtual void OnParentRightToLeftChanged(EventArgs e) { } protected virtual void OnTextChanged(EventArgs e) => TextChanged?.Invoke(this, e); protected virtual void OnEnabledChanged(EventArgs e) => EnabledChanged?.Invoke(this, e); protected virtual void OnVisibleChanged(EventArgs e) => VisibleChanged?.Invoke(this, e); protected virtual void OnControlAdded(ControlEventArgs e) => ControlAdded?.Invoke(this, e); protected virtual void OnControlRemoved(ControlEventArgs e) => ControlRemoved?.Invoke(this, e); protected virtual object GetService(object service) => null; protected virtual object GetService(Type serviceType) => null; protected void SetStyle(ControlStyles flag, bool value) { controlStyle = (value ? (controlStyle | flag) : (controlStyle & ~flag)); // FR designer's smarttag button depends on this control.Focusable = (controlStyle & ControlStyles.Selectable) != 0; } protected virtual void ScaleCore(float dx, float dy) { if (control is not Window) Location = new System.Drawing.Point((int)(Location.X * dx), (int)(Location.Y * dy)); if (!AutoSize) ClientSize = new System.Drawing.Size((int)(ClientSize.Width * dx), (int)(ClientSize.Height * dy)); } protected virtual bool IsInputKey(Keys keyData) => false; protected virtual bool ProcessDialogKey(Keys keyData) => false; protected void SetControl(System.Windows.Controls.Control control) { this.control = control; control.Tag = this; control.HorizontalAlignment = System.Windows.HorizontalAlignment.Left; control.VerticalAlignment = System.Windows.VerticalAlignment.Top; Controls = new ControlCollection(this); WireEvents(); AllowDrop = false; } protected virtual void Dispose(bool disposing) { if (!disposing) return; if (parent != null) parent.Controls.Remove(this); for (int i = 0; i < Controls.Count; i++) { Control control = Controls[i]; control.parent = null; control.Dispose(); } isDisposed = true; bitmapBuffer?.Dispose(); bitmapBuffer = null; Disposed?.Invoke(this, EventArgs.Empty); } protected virtual System.Drawing.Size DefaultSize => new Drawing.Size(Width, Height); public void Dispose() { Dispose(true); } public void DoDragDrop(object data, DragDropEffects allowedEffects) => System.Windows.DragDrop.DoDragDrop(control, data, (System.Windows.DragDropEffects)allowedEffects); // this is used in drag/drop operations which already return client coords, so do nothing public System.Drawing.Point PointToClient(System.Drawing.Point pos) => pos; public void Scale(float dx, float dy) { if (dx == 1 && dy == 1) return; SuspendLayout(); ScaleCore(dx, dy); ResetAutoSizeValue(false); for (int i = 0; i < Controls.Count; i++) { Control c = Controls[i]; c.Scale(dx, dy); } ResumeLayout(false); OnLayout(); } private void EnumControls(Control control, Action action) { action(control); foreach (Control c in control.Controls) EnumControls(c, action); } public void Scale(SizeF scale) { if (this.DeviceDpi != Helper.ScreenDpi) { float sc = (float)Helper.ScreenDpi / 96; Scale(sc, sc); sc = scale.Width / sc; // we have to return correct width & height to the calling method width = (int)(width * sc); height = (int)(height * sc); // necessary for cases like 120->144 scale (case: FR connection form) int saveWidth = width; int saveHeight = height; bool layoutComplete = false; EnumControls(this, c => c.SuspendLayout()); control.Dispatcher.InvokeAsync(() => { Size = new Drawing.Size(saveWidth, saveHeight); EnumControls(this, c => { if (c is ContainerControl ctl) ctl.ScaleInternals(sc, sc); c.ResumeLayout(false); c.Refresh(); }); layoutComplete = true; }, Threading.DispatcherPriority.Render); // return to the calling method when layout completed to make rtl working properly (case: connection form & rtl & pmV2) while (!layoutComplete) Application.DoEvents(); return; } Scale(scale.Width, scale.Height); } internal void BeforeDpiChange() { SuspendLayout(); } internal virtual void AfterDpiChange(float rescale) { if (!double.IsNaN(control.Width)) width = (int)(control.Width * DpiScale); else width = (int)(control.ActualWidth * DpiScale); if (!double.IsNaN(control.Height)) height = (int)(control.Height * DpiScale); else height = (int)(control.ActualHeight * DpiScale); ResetAutoSizeValue(false); ResumeLayout(false); OnLayout(); } public void SetBounds(int left, int top, int width, int height) => Bounds = new Rectangle(left, top, width, height); public void Focus() => control.Focus(); public void Show() => Visible = true; public void Hide() => Visible = false; public void BringToFront() => Parent?.Controls.SetChildIndex(this, 0); public void SendToBack() => Parent?.Controls.SetChildIndex(this, Parent.Controls.Count - 1); public virtual void Select() { } public virtual void Refresh() { if (isPainting) return; control.InvalidateVisual(); } public void Invalidate() { Refresh(); OnInvalidated(new InvalidateEventArgs(DisplayRectangle)); } public void Invalidate(bool flag) => Invalidate(); public void Invalidate(Rectangle rect) => Invalidate(); public void Update() { } public Form FindForm() => System.Windows.Window.GetWindow(control)?.Tag as Form; public void SuspendLayout() => LayoutEngine.SuspendLayout(); public virtual void ResumeLayout(bool resume) { LayoutEngine.ResumeLayout(resume); if (!resume) UpdateLayout(); // update docked controls (case: barcode editor form with tvData docked incorrectly) } public void ResumeLayout() => ResumeLayout(true); public void PerformLayout() { } public void CreateControl() { } public void ResetText() => Text = ""; public Graphics CreateGraphics() => Graphics.FromImage(new Bitmap(1, 1)); // TODO: FIXME all use cases public void Invoke(Action action) => control.Dispatcher.Invoke(action); public virtual Drawing.Point PointToScreen(Drawing.Point point) { if (!control.IsLoaded) return point; var pt = control.PointToScreen(new Point(point.X / DpiScale, point.Y / DpiScale)); // pt is already in screen physical coords, no need to dpi-scale it return new Drawing.Point((int)(pt.X), (int)(pt.Y)); } public virtual void DrawToBitmap(Bitmap bitmap, Rectangle rect) { Helper.DrawControl(control, bitmap, Width, Height, DeviceDpi); } public Control() { LayoutEngine = new(this); Anchor = AnchorStyles.Left | AnchorStyles.Top; controlStyle = ControlStyles.Selectable; SetControl(new OwnerDrawControl(this)); } ~Control() { Dispose(false); } } }