123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869 |
- using System.Collections.Generic;
- using System.Windows.Input;
- namespace System.Windows.Forms
- {
- public static class DpiRescaler
- {
- public static void Install(Window window, Control control, System.Windows.UIElement wpfControl, EventHandler handler)
- {
- if (window == null)
- return;
- window.DpiChanged += (sender, e) =>
- {
- // this event is fired when current dpi is changed:
- // - when you move a window to another monitor with different dpi;
- // - when you change dpi while window is open;
- // - when you have changed dpi while app is running and you open a new window;
- // - if you moved an app to another monitor and open a new window.
- // For the two latter cases, window is created using the default dpi (default monitor dpi).
- // When a window is about to show, WPF scales its controls and fires DpiChanged event.
- // We need some more work to make docking/anchor system behave correctly on rescale.
- // we need only event coming from the window
- if (e.Source == window)
- {
- var controls = new List<Control>();
- // suspend layout of all form controls before proceed.
- EnumControls(control, c =>
- {
- c.BeforeDpiChange();
- // collect processed controls
- controls.Add(c);
- });
- // hide the root form control to avoid weird visual effects (partially rendered controls during dpi change)
- wpfControl.Visibility = Visibility.Hidden;
- // this action will be performed after the form is rescaled completely. Async is required
- window.Dispatcher.InvokeAsync(() =>
- {
- // original code was:
- // float rescale = DeviceDpi / AutoScaleDimensions.Width;
- // we need to check this case (bug: FR dialog form designer does not scale properly)
- float rescale = control is ContainerControl container ?
- container.DeviceDpi / container.AutoScaleDimensions.Width :
- (float)(e.NewDpi.PixelsPerInchX / e.OldDpi.PixelsPerInchX);
- // process controls in reverse order to match Suspend/Resume calls (important, case: StdReportWizard bad rescale on pmV2)
- controls.Reverse();
- controls.ForEach(c => c.AfterDpiChange(rescale));
- handler(control, e);
- // restore the root control visibility, do it cool
- wpfControl.Visibility = Visibility.Visible;
- wpfControl.AnimateOpacity(0, 1, 100);
- // hiding/showing the container causes focus lost. Restore the focus (issue with FR text editor showing on non-primary monitor)
- FocusManager.GetFocusedElement(window)?.Focus();
- });
-
- void EnumControls(Control control, Action<Control> action)
- {
- action(control);
- foreach (Control c in control.Controls)
- EnumControls(c, action);
- }
- }
- };
- }
- }
- }
|