diff --git a/Documents/Changelog/Changelog.md b/Documents/Changelog/Changelog.md index f951d089b..0557ed46e 100644 --- a/Documents/Changelog/Changelog.md +++ b/Documents/Changelog/Changelog.md @@ -4,6 +4,8 @@ ## 2026-11-xx - Build 2611 (V110 Nightly) - November 2026 +* Resolved/Implemented [#2844](https://github.com/Krypton-Suite/Standard-Toolkit/issues/2844), Touchscreen High DPI scaling +* Implemented [#2808](https://github.com/Krypton-Suite/Standard-Toolkit/issues/2808), Move `KryptonToastNotification` feature to `Krypton.Utilities` * Implemented [#2572](https://github.com/Krypton-Suite/Standard-Toolkit/issues/2572), Autocomplete control/menu - To use, you will need to download the `Krypton.Standard.Toolkit` NuGet package, as this control is part of the `Krypton.Utilities` assembly. * Implemented [#2812](https://github.com/Krypton-Suite/Standard-Toolkit/issues/2812), Code Editor Control diff --git a/Source/Krypton Components/Krypton.Toolkit/Controls Toolkit/KryptonManager.cs b/Source/Krypton Components/Krypton.Toolkit/Controls Toolkit/KryptonManager.cs index 38271bd39..4ec2f1113 100644 --- a/Source/Krypton Components/Krypton.Toolkit/Controls Toolkit/KryptonManager.cs +++ b/Source/Krypton Components/Krypton.Toolkit/Controls Toolkit/KryptonManager.cs @@ -37,6 +37,8 @@ public sealed class KryptonManager : Component private static System.Threading.Timer? _touchscreenDetectionTimer; private static bool _lastDetectedTouchscreenState = false; private static Font? _baseFont; + private static float _cachedDpiX = 0f; + private static float _cachedDpiY = 0f; // Initialize the default modes @@ -1615,4 +1617,390 @@ private static void OnGlobalTouchscreenSupportChanged(EventArgs e) #endregion + #region DPI-Aware Helper Methods + + /// + /// Gets the current DPI scaling factor for the X axis (horizontal). + /// Returns 1.0 for 96 DPI (100% scaling), 1.25 for 120 DPI (125% scaling), etc. + /// Uses the primary monitor's DPI. For per-monitor DPI awareness, use the overload that accepts a window handle. + /// + /// The DPI scaling factor for the X axis. + public static float GetDpiFactorX() + { + if (_cachedDpiX <= 0.1f) + { + var screenDc = PI.GetDC(IntPtr.Zero); + + if (screenDc != IntPtr.Zero) + { + _cachedDpiX = PI.GetDeviceCaps(screenDc, PI.DeviceCap.LOGPIXELSX) / 96f; + + PI.ReleaseDC(IntPtr.Zero, screenDc); + } + else + { + using (Graphics gfx = Graphics.FromHwnd(IntPtr.Zero)) + { + _cachedDpiX = gfx.DpiX / 96f; + } + } + } + + return _cachedDpiX; + } + + /// + /// Gets the DPI scaling factor for the X axis (horizontal) for a specific window. + /// This method supports per-monitor DPI awareness by using the window's monitor DPI. + /// Returns 1.0 for 96 DPI (100% scaling), 1.25 for 120 DPI (125% scaling), etc. + /// + /// Window handle to get the DPI for. If IntPtr.Zero, falls back to primary monitor DPI. + /// The DPI scaling factor for the X axis. + public static float GetDpiFactorX(IntPtr hWnd) + { + if (hWnd == IntPtr.Zero) + { + return GetDpiFactorX(); + } + + // Try to use GetDpiForWindow for per-monitor DPI awareness (Windows 10 version 1607+) + try + { + uint dpi = PI.GetDpiForWindow(hWnd); + + if (dpi > 0) + { + return dpi / 96f; + } + } + catch + { + // GetDpiForWindow may not be available on older Windows versions + } + + // Fallback to window's Graphics DPI + try + { + using (Graphics graphics = Graphics.FromHwnd(hWnd)) + { + return graphics.DpiX / 96f; + } + } + catch + { + // Final fallback to primary monitor + return GetDpiFactorX(); + } + } + + /// + /// Gets the current DPI scaling factor for the Y axis (vertical). + /// Returns 1.0 for 96 DPI (100% scaling), 1.25 for 120 DPI (125% scaling), etc. + /// Uses the primary monitor's DPI. For per-monitor DPI awareness, use the overload that accepts a window handle. + /// + /// The DPI scaling factor for the Y axis. + public static float GetDpiFactorY() + { + if (_cachedDpiY <= 0.1f) + { + var screenDc = PI.GetDC(IntPtr.Zero); + if (screenDc != IntPtr.Zero) + { + _cachedDpiY = PI.GetDeviceCaps(screenDc, PI.DeviceCap.LOGPIXELSY) / 96f; + PI.ReleaseDC(IntPtr.Zero, screenDc); + } + else + { + // Fallback method + using Graphics graphics = Graphics.FromHwnd(IntPtr.Zero); + _cachedDpiY = graphics.DpiY / 96f; + } + } + + return _cachedDpiY; + } + + /// + /// Gets the DPI scaling factor for the Y axis (vertical) for a specific window. + /// This method supports per-monitor DPI awareness by using the window's monitor DPI. + /// Returns 1.0 for 96 DPI (100% scaling), 1.25 for 120 DPI (125% scaling), etc. + /// + /// Window handle to get the DPI for. If IntPtr.Zero, falls back to primary monitor DPI. + /// The DPI scaling factor for the Y axis. + public static float GetDpiFactorY(IntPtr hWnd) + { + if (hWnd == IntPtr.Zero) + { + return GetDpiFactorY(); + } + + // Try to use GetDpiForWindow for per-monitor DPI awareness (Windows 10 version 1607+) + try + { + uint dpi = PI.GetDpiForWindow(hWnd); + if (dpi > 0) + { + return dpi / 96f; + } + } + catch + { + // GetDpiForWindow may not be available on older Windows versions + } + + // Fallback to window's Graphics DPI + try + { + using Graphics graphics = Graphics.FromHwnd(hWnd); + return graphics.DpiY / 96f; + } + catch + { + // Final fallback to primary monitor + return GetDpiFactorY(); + } + } + + /// + /// Gets the current DPI scaling factor (average of X and Y axes). + /// Useful when uniform scaling is assumed. + /// Uses the primary monitor's DPI. For per-monitor DPI awareness, use the overload that accepts a window handle. + /// + /// The average DPI scaling factor. + public static float GetDpiFactor() => (GetDpiFactorX() + GetDpiFactorY()) / 2f; + + /// + /// Gets the DPI scaling factor (average of X and Y axes) for a specific window. + /// This method supports per-monitor DPI awareness by using the window's monitor DPI. + /// + /// Window handle to get the DPI for. If IntPtr.Zero, falls back to primary monitor DPI. + /// The average DPI scaling factor. + public static float GetDpiFactor(IntPtr hWnd) => (GetDpiFactorX(hWnd) + GetDpiFactorY(hWnd)) / 2f; + + /// + /// Gets the combined scaling factor (DPI × Touchscreen) for the X axis. + /// This represents the total scaling that will be applied to control sizes. + /// Uses the primary monitor's DPI. For per-monitor DPI awareness, use the overload that accepts a window handle. + /// + /// The combined scaling factor for the X axis. + public static float GetCombinedScaleFactorX() + { + var dpiFactor = GetDpiFactorX(); + var touchscreenFactor = TouchscreenScaleFactor; + return dpiFactor * touchscreenFactor; + } + + /// + /// Gets the combined scaling factor (DPI × Touchscreen) for the X axis for a specific window. + /// This method supports per-monitor DPI awareness, which is important for touchscreen support on high DPI displays. + /// This represents the total scaling that will be applied to control sizes. + /// + /// Window handle to get the DPI for. If IntPtr.Zero, falls back to primary monitor DPI. + /// The combined scaling factor for the X axis. + public static float GetCombinedScaleFactorX(IntPtr hWnd) + { + var dpiFactor = GetDpiFactorX(hWnd); + var touchscreenFactor = TouchscreenScaleFactor; + return dpiFactor * touchscreenFactor; + } + + /// + /// Gets the combined scaling factor (DPI × Touchscreen) for the Y axis. + /// This represents the total scaling that will be applied to control sizes. + /// Uses the primary monitor's DPI. For per-monitor DPI awareness, use the overload that accepts a window handle. + /// + /// The combined scaling factor for the Y axis. + public static float GetCombinedScaleFactorY() + { + var dpiFactor = GetDpiFactorY(); + var touchscreenFactor = TouchscreenScaleFactor; + return dpiFactor * touchscreenFactor; + } + + /// + /// Gets the combined scaling factor (DPI × Touchscreen) for the Y axis for a specific window. + /// This method supports per-monitor DPI awareness, which is important for touchscreen support on high DPI displays. + /// This represents the total scaling that will be applied to control sizes. + /// + /// Window handle to get the DPI for. If IntPtr.Zero, falls back to primary monitor DPI. + /// The combined scaling factor for the Y axis. + public static float GetCombinedScaleFactorY(IntPtr hWnd) + { + var dpiFactor = GetDpiFactorY(hWnd); + var touchscreenFactor = TouchscreenScaleFactor; + return dpiFactor * touchscreenFactor; + } + + /// + /// Gets the combined scaling factor (DPI × Touchscreen) as an average. + /// Useful when uniform scaling is assumed. + /// Uses the primary monitor's DPI. For per-monitor DPI awareness, use the overload that accepts a window handle. + /// + /// The average combined scaling factor. + public static float GetCombinedScaleFactor() => (GetCombinedScaleFactorX() + GetCombinedScaleFactorY()) / 2f; + + /// + /// Gets the combined scaling factor (DPI × Touchscreen) as an average for a specific window. + /// This method supports per-monitor DPI awareness, which is important for touchscreen support on high DPI displays. + /// Useful when uniform scaling is assumed. + /// + /// Window handle to get the DPI for. If IntPtr.Zero, falls back to primary monitor DPI. + /// The average combined scaling factor. + public static float GetCombinedScaleFactor(IntPtr hWnd) => (GetCombinedScaleFactorX(hWnd) + GetCombinedScaleFactorY(hWnd)) / 2f; + + /// + /// Scales a single value by the current DPI factor. + /// + /// The value to scale. + /// The scaled value. + public static int ScaleValueByDpi(int value) => (int)Math.Round(value * GetDpiFactor()); + + /// + /// Scales a single value by the current DPI factor. + /// + /// The value to scale. + /// The scaled value. + public static float ScaleValueByDpi(float value) => value * GetDpiFactor(); + + /// + /// Scales a single value by the combined DPI and touchscreen factor. + /// + /// The value to scale. + /// The scaled value. + public static int ScaleValueByDpiAndTouchscreen(int value) => (int)Math.Round(value * GetCombinedScaleFactor()); + + /// + /// Scales a single value by the combined DPI and touchscreen factor. + /// + /// The value to scale. + /// The scaled value. + public static float ScaleValueByDpiAndTouchscreen(float value) => value * GetCombinedScaleFactor(); + + /// + /// Scales a Size by the current DPI factors (X and Y separately). + /// + /// The size to scale. + /// The scaled size. + public static Size ScaleSizeByDpi(Size size) => new Size( + (int)Math.Round(size.Width * GetDpiFactorX()), + (int)Math.Round(size.Height * GetDpiFactorY())); + + /// + /// Scales a SizeF by the current DPI factors (X and Y separately). + /// + /// The size to scale. + /// The scaled size. + public static SizeF ScaleSizeByDpi(SizeF size) => new SizeF( + size.Width * GetDpiFactorX(), + size.Height * GetDpiFactorY()); + + /// + /// Scales a Size by the combined DPI and touchscreen factors (X and Y separately). + /// + /// The size to scale. + /// The scaled size. + public static Size ScaleSizeByDpiAndTouchscreen(Size size) => new Size( + (int)Math.Round(size.Width * GetCombinedScaleFactorX()), + (int)Math.Round(size.Height * GetCombinedScaleFactorY())); + + /// + /// Scales a SizeF by the combined DPI and touchscreen factors (X and Y separately). + /// + /// The size to scale. + /// The scaled size. + public static SizeF ScaleSizeByDpiAndTouchscreen(SizeF size) => new SizeF( + size.Width * GetCombinedScaleFactorX(), + size.Height * GetCombinedScaleFactorY()); + + /// + /// Scales a Point by the current DPI factors (X and Y separately). + /// + /// The point to scale. + /// The scaled point. + public static Point ScalePointByDpi(Point point) => new Point( + (int)Math.Round(point.X * GetDpiFactorX()), + (int)Math.Round(point.Y * GetDpiFactorY())); + + /// + /// Scales a PointF by the current DPI factors (X and Y separately). + /// + /// The point to scale. + /// The scaled point. + public static PointF ScalePointByDpi(PointF point) => new PointF( + point.X * GetDpiFactorX(), + point.Y * GetDpiFactorY()); + + /// + /// Scales a Point by the combined DPI and touchscreen factors (X and Y separately). + /// + /// The point to scale. + /// The scaled point. + public static Point ScalePointByDpiAndTouchscreen(Point point) => new Point( + (int)Math.Round(point.X * GetCombinedScaleFactorX()), + (int)Math.Round(point.Y * GetCombinedScaleFactorY())); + + /// + /// Scales a PointF by the combined DPI and touchscreen factors (X and Y separately). + /// + /// The point to scale. + /// The scaled point. + public static PointF ScalePointByDpiAndTouchscreen(PointF point) => new PointF( + point.X * GetCombinedScaleFactorX(), + point.Y * GetCombinedScaleFactorY()); + + /// + /// Scales a Rectangle by the current DPI factors (X and Y separately). + /// + /// The rectangle to scale. + /// The scaled rectangle. + public static Rectangle ScaleRectangleByDpi(Rectangle rect) => new Rectangle( + (int)Math.Round(rect.X * GetDpiFactorX()), + (int)Math.Round(rect.Y * GetDpiFactorY()), + (int)Math.Round(rect.Width * GetDpiFactorX()), + (int)Math.Round(rect.Height * GetDpiFactorY())); + + /// + /// Scales a RectangleF by the current DPI factors (X and Y separately). + /// + /// The rectangle to scale. + /// The scaled rectangle. + public static RectangleF ScaleRectangleByDpi(RectangleF rect) => new RectangleF( + rect.X * GetDpiFactorX(), + rect.Y * GetDpiFactorY(), + rect.Width * GetDpiFactorX(), + rect.Height * GetDpiFactorY()); + + /// + /// Scales a Rectangle by the combined DPI and touchscreen factors (X and Y separately). + /// + /// The rectangle to scale. + /// The scaled rectangle. + public static Rectangle ScaleRectangleByDpiAndTouchscreen(Rectangle rect) => new Rectangle( + (int)Math.Round(rect.X * GetCombinedScaleFactorX()), + (int)Math.Round(rect.Y * GetCombinedScaleFactorY()), + (int)Math.Round(rect.Width * GetCombinedScaleFactorX()), + (int)Math.Round(rect.Height * GetCombinedScaleFactorY())); + + /// + /// Scales a RectangleF by the combined DPI and touchscreen factors (X and Y separately). + /// + /// The rectangle to scale. + /// The scaled rectangle. + public static RectangleF ScaleRectangleByDpiAndTouchscreen(RectangleF rect) => new RectangleF( + rect.X * GetCombinedScaleFactorX(), + rect.Y * GetCombinedScaleFactorY(), + rect.Width * GetCombinedScaleFactorX(), + rect.Height * GetCombinedScaleFactorY()); + + /// + /// Invalidates the cached DPI factors, forcing them to be recalculated on the next access. + /// Call this method when the DPI changes (e.g., when the window is moved to a different monitor). + /// + public static void InvalidateDpiCache() + { + _cachedDpiX = 0f; + _cachedDpiY = 0f; + } + + #endregion } \ No newline at end of file diff --git a/Source/Krypton Components/Krypton.Toolkit/Controls Visuals/VisualForm.cs b/Source/Krypton Components/Krypton.Toolkit/Controls Visuals/VisualForm.cs index d4504bb56..fd6720b1d 100644 --- a/Source/Krypton Components/Krypton.Toolkit/Controls Visuals/VisualForm.cs +++ b/Source/Krypton Components/Krypton.Toolkit/Controls Visuals/VisualForm.cs @@ -1931,6 +1931,45 @@ private void OnJumpListChanged() private void UpdateDpiFactors() { + // Invalidate the global DPI cache to ensure fresh values are calculated + KryptonManager.InvalidateDpiCache(); + + // Use per-monitor DPI for proper high DPI and touchscreen scaling support + IntPtr hWnd = IsHandleCreated ? Handle : IntPtr.Zero; + + if (hWnd != IntPtr.Zero) + { + try + { + // Try to use GetDpiForWindow for per-monitor DPI awareness (Windows 10 version 1607+) + uint dpi = PI.GetDpiForWindow(hWnd); + if (dpi > 0) + { + FactorDpiX = dpi / 96f; + FactorDpiY = dpi / 96f; + return; + } + } + catch + { + // GetDpiForWindow may not be available on older Windows versions + } + + // Fallback to window's Graphics DPI + try + { + using Graphics graphics = Graphics.FromHwnd(hWnd); + FactorDpiX = graphics.DpiX / 96f; + FactorDpiY = graphics.DpiY / 96f; + return; + } + catch + { + // Continue to primary monitor fallback + } + } + + // Fallback // Do not use the control dpi, as these values are being used to target the screen IntPtr screenDc = PI.GetDC(IntPtr.Zero); if (screenDc != IntPtr.Zero) diff --git a/Source/Krypton Components/Krypton.Toolkit/View Base/ViewBase.cs b/Source/Krypton Components/Krypton.Toolkit/View Base/ViewBase.cs index 1690ab4c3..214b5f32a 100644 --- a/Source/Krypton Components/Krypton.Toolkit/View Base/ViewBase.cs +++ b/Source/Krypton Components/Krypton.Toolkit/View Base/ViewBase.cs @@ -240,6 +240,53 @@ public float FactorDpiX private void InitialiseFactors() { + // Try to get per-monitor DPI from the owning control for proper high DPI support + Control? owningControl = OwningControl; + IntPtr hWnd = IntPtr.Zero; + + if (owningControl != null && owningControl.IsHandleCreated) + { + hWnd = owningControl.Handle; + } + else if (Component is Control componentControl && componentControl.IsHandleCreated) + { + hWnd = componentControl.Handle; + } + + // Use per-monitor DPI if we have a window handle (supports high DPI and touchscreen scaling) + if (hWnd != IntPtr.Zero) + { + try + { + // Try to use GetDpiForWindow for per-monitor DPI awareness (Windows 10 version 1607+) + uint dpi = PI.GetDpiForWindow(hWnd); + if (dpi > 0) + { + _factorDpiX = dpi / 96f; + _factorDpiY = dpi / 96f; + return; + } + } + catch + { + // GetDpiForWindow may not be available on older Windows versions + } + + // Fallback to window's Graphics DPI + try + { + using Graphics graphics = Graphics.FromHwnd(hWnd); + _factorDpiX = graphics.DpiX / 96f; + _factorDpiY = graphics.DpiY / 96f; + return; + } + catch + { + // Continue to primary monitor fallback + } + } + + // Fallback // This does mean that the app will not change it's dpi awareness until restarted ! // Do not use the control dpi, as these values are being used to target the screen var screenDc = PI.GetDC(IntPtr.Zero); @@ -274,6 +321,16 @@ public float FactorDpiY } } + /// + /// Invalidates the cached DPI factors, forcing them to be recalculated on the next access. + /// Call this method when the DPI changes (e.g., when the window is moved to a different monitor). + /// + public void InvalidateDpiFactors() + { + _factorDpiX = 0f; + _factorDpiY = 0f; + } + #endregion #region Component diff --git a/Source/Krypton Components/TestForm/StartScreen.cs b/Source/Krypton Components/TestForm/StartScreen.cs index d15894408..5c31e6fd6 100644 --- a/Source/Krypton Components/TestForm/StartScreen.cs +++ b/Source/Krypton Components/TestForm/StartScreen.cs @@ -84,6 +84,7 @@ private void AddButtons() CreateButton("Taskbar Overlay Icon Test", "Comprehensive demonstration of taskbar overlay icons on KryptonForm with configurable icons, descriptions, and interactive examples.", typeof(TaskbarOverlayIconTest)); CreateButton("Theme Controls", string.Empty, typeof(ThemeControlExamples)); CreateButton("TextBox Validating Test", "Tests fix for Validating event duplication bug #2801", typeof(KryptonTextBoxValidatingTest)); + CreateButton("Touchscreen + High DPI Demo", "Comprehensive demonstration of touchscreen support with per-monitor high DPI scaling (Issue #2844).", typeof(TouchscreenHighDpiDemo)); CreateButton("RichTextBox Formatting Test", "Tests fix for RichTextBox formatting preservation when palette changes (Issue #2832)", typeof(RichTextBoxFormattingTest)); CreateButton("RTL Layout Test", "Test for RTL compliance", typeof(RTLFormBorderTest)); CreateButton("Toast", "For breakfast....?", typeof(ToastNotificationTestChoice)); diff --git a/Source/Krypton Components/TestForm/TouchscreenHighDpiDemo.Designer.cs b/Source/Krypton Components/TestForm/TouchscreenHighDpiDemo.Designer.cs new file mode 100644 index 000000000..ac5b75c11 --- /dev/null +++ b/Source/Krypton Components/TestForm/TouchscreenHighDpiDemo.Designer.cs @@ -0,0 +1,676 @@ +namespace TestForm +{ + partial class TouchscreenHighDpiDemo + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + _dpiMonitorTimer?.Stop(); + _dpiMonitorTimer?.Dispose(); + KryptonManager.GlobalTouchscreenSupportChanged -= OnGlobalTouchscreenSupportChanged; + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.grpControls = new Krypton.Toolkit.KryptonGroupBox(); + this.grpAdvancedControls = new Krypton.Toolkit.KryptonGroupBox(); + this.workspace = new Krypton.Workspace.KryptonWorkspace(); + this.navigator = new Krypton.Navigator.KryptonNavigator(); + this.grpInputControls = new Krypton.Toolkit.KryptonGroupBox(); + this.txtInput = new Krypton.Toolkit.KryptonTextBox(); + this.txtNumeric = new Krypton.Toolkit.KryptonNumericUpDown(); + this.cmbOptions = new Krypton.Toolkit.KryptonComboBox(); + this.grpButtons = new Krypton.Toolkit.KryptonGroupBox(); + this.btnStandard = new Krypton.Toolkit.KryptonButton(); + this.btnPrimary = new Krypton.Toolkit.KryptonButton(); + this.btnSuccess = new Krypton.Toolkit.KryptonButton(); + this.grpCheckboxes = new Krypton.Toolkit.KryptonGroupBox(); + this.chkOption1 = new Krypton.Toolkit.KryptonCheckBox(); + this.chkOption2 = new Krypton.Toolkit.KryptonCheckBox(); + this.chkOption3 = new Krypton.Toolkit.KryptonCheckBox(); + this.grpRadioButtons = new Krypton.Toolkit.KryptonGroupBox(); + this.radioOption1 = new Krypton.Toolkit.KryptonRadioButton(); + this.radioOption2 = new Krypton.Toolkit.KryptonRadioButton(); + this.radioOption3 = new Krypton.Toolkit.KryptonRadioButton(); + this.grpOtherControls = new Krypton.Toolkit.KryptonGroupBox(); + this.progressBar = new Krypton.Toolkit.KryptonProgressBar(); + this.trackBar = new Krypton.Toolkit.KryptonTrackBar(); + this.grpSettings = new Krypton.Toolkit.KryptonGroupBox(); + this.grpDpiInfo = new Krypton.Toolkit.KryptonGroupBox(); + this.lblScalingExample = new Krypton.Toolkit.KryptonLabel(); + this.lblDpiWarning = new Krypton.Toolkit.KryptonLabel(); + this.lblCombinedPerMonitor = new Krypton.Toolkit.KryptonLabel(); + this.lblCombinedPrimary = new Krypton.Toolkit.KryptonLabel(); + this.lblDpiActual = new Krypton.Toolkit.KryptonLabel(); + this.lblDpiPerMonitor = new Krypton.Toolkit.KryptonLabel(); + this.lblDpiPrimary = new Krypton.Toolkit.KryptonLabel(); + this.btnRefreshDpi = new Krypton.Toolkit.KryptonButton(); + this.lblStatus = new Krypton.Toolkit.KryptonLabel(); + this.btnToggle = new Krypton.Toolkit.KryptonButton(); + this.btnApplyPreset75 = new Krypton.Toolkit.KryptonButton(); + this.btnApplyPreset50 = new Krypton.Toolkit.KryptonButton(); + this.btnApplyPreset25 = new Krypton.Toolkit.KryptonButton(); + this.btnResetScale = new Krypton.Toolkit.KryptonButton(); + this.lblScaleValue = new Krypton.Toolkit.KryptonLabel(); + this.trackScaleFactor = new Krypton.Toolkit.KryptonTrackBar(); + this.lblScaleFactor = new Krypton.Toolkit.KryptonLabel(); + this.chkEnableTouchscreen = new Krypton.Toolkit.KryptonCheckBox(); + this.chkEnableFontScaling = new Krypton.Toolkit.KryptonCheckBox(); + this.lblFontScaleFactor = new Krypton.Toolkit.KryptonLabel(); + this.trackFontScaleFactor = new Krypton.Toolkit.KryptonTrackBar(); + this.lblFontScaleValue = new Krypton.Toolkit.KryptonLabel(); + ((System.ComponentModel.ISupportInitialize)(this.grpControls)).BeginInit(); + ((System.ComponentModel.ISupportInitialize)(this.grpControls.Panel)).BeginInit(); + this.grpControls.Panel.SuspendLayout(); + this.grpControls.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)(this.grpInputControls)).BeginInit(); + ((System.ComponentModel.ISupportInitialize)(this.grpInputControls.Panel)).BeginInit(); + this.grpInputControls.Panel.SuspendLayout(); + this.grpInputControls.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)(this.grpButtons)).BeginInit(); + ((System.ComponentModel.ISupportInitialize)(this.grpButtons.Panel)).BeginInit(); + this.grpButtons.Panel.SuspendLayout(); + this.grpButtons.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)(this.grpCheckboxes)).BeginInit(); + ((System.ComponentModel.ISupportInitialize)(this.grpCheckboxes.Panel)).BeginInit(); + this.grpCheckboxes.Panel.SuspendLayout(); + this.grpCheckboxes.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)(this.grpRadioButtons)).BeginInit(); + ((System.ComponentModel.ISupportInitialize)(this.grpRadioButtons.Panel)).BeginInit(); + this.grpRadioButtons.Panel.SuspendLayout(); + this.grpRadioButtons.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)(this.grpOtherControls)).BeginInit(); + ((System.ComponentModel.ISupportInitialize)(this.grpOtherControls.Panel)).BeginInit(); + this.grpOtherControls.Panel.SuspendLayout(); + this.grpOtherControls.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)(this.grpSettings)).BeginInit(); + ((System.ComponentModel.ISupportInitialize)(this.grpSettings.Panel)).BeginInit(); + this.grpSettings.Panel.SuspendLayout(); + this.grpSettings.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)(this.grpDpiInfo)).BeginInit(); + ((System.ComponentModel.ISupportInitialize)(this.grpDpiInfo.Panel)).BeginInit(); + this.grpDpiInfo.Panel.SuspendLayout(); + this.grpDpiInfo.SuspendLayout(); + this.SuspendLayout(); + // + // grpControls + // + this.grpControls.Dock = System.Windows.Forms.DockStyle.Fill; + this.grpControls.Location = new System.Drawing.Point(0, 0); + this.grpControls.Name = "grpControls"; + this.grpControls.Size = new System.Drawing.Size(1000, 500); + this.grpControls.Panel.AutoScroll = true; + this.grpControls.TabIndex = 0; + this.grpControls.Values.Heading = "Control Examples (These scale with High DPI + Touchscreen support)"; + // + // grpControls.Panel + // + this.grpControls.Panel.Controls.Add(this.grpAdvancedControls); + this.grpControls.Panel.Controls.Add(this.grpOtherControls); + this.grpControls.Panel.Controls.Add(this.grpRadioButtons); + this.grpControls.Panel.Controls.Add(this.grpCheckboxes); + this.grpControls.Panel.Controls.Add(this.grpButtons); + this.grpControls.Panel.Controls.Add(this.grpInputControls); + // + // grpInputControls + // + this.grpInputControls.Location = new System.Drawing.Point(15, 15); + this.grpInputControls.Name = "grpInputControls"; + this.grpInputControls.Size = new System.Drawing.Size(400, 120); + this.grpInputControls.TabIndex = 0; + this.grpInputControls.Values.Heading = "Input Controls"; + // + // grpInputControls.Panel + // + this.grpInputControls.Panel.Controls.Add(this.cmbOptions); + this.grpInputControls.Panel.Controls.Add(this.txtNumeric); + this.grpInputControls.Panel.Controls.Add(this.txtInput); + // + // txtInput + // + this.txtInput.Location = new System.Drawing.Point(15, 20); + this.txtInput.Name = "txtInput"; + this.txtInput.Size = new System.Drawing.Size(370, 27); + this.txtInput.TabIndex = 0; + // + // txtNumeric + // + this.txtNumeric.Location = new System.Drawing.Point(15, 55); + this.txtNumeric.Name = "txtNumeric"; + this.txtNumeric.Size = new System.Drawing.Size(180, 27); + this.txtNumeric.TabIndex = 1; + // + // cmbOptions + // + this.cmbOptions.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.cmbOptions.DropDownWidth = 370; + this.cmbOptions.Location = new System.Drawing.Point(205, 55); + this.cmbOptions.Name = "cmbOptions"; + this.cmbOptions.Size = new System.Drawing.Size(180, 27); + this.cmbOptions.TabIndex = 2; + // + // grpButtons + // + this.grpButtons.Location = new System.Drawing.Point(430, 15); + this.grpButtons.Name = "grpButtons"; + this.grpButtons.Size = new System.Drawing.Size(430, 120); + this.grpButtons.TabIndex = 1; + this.grpButtons.Values.Heading = "Buttons"; + // + // grpButtons.Panel + // + this.grpButtons.Panel.Controls.Add(this.btnSuccess); + this.grpButtons.Panel.Controls.Add(this.btnPrimary); + this.grpButtons.Panel.Controls.Add(this.btnStandard); + // + // btnStandard + // + this.btnStandard.Location = new System.Drawing.Point(15, 20); + this.btnStandard.Name = "btnStandard"; + this.btnStandard.Size = new System.Drawing.Size(120, 35); + this.btnStandard.TabIndex = 0; + this.btnStandard.Values.Text = "Standard"; + // + // btnPrimary + // + this.btnPrimary.Location = new System.Drawing.Point(150, 20); + this.btnPrimary.Name = "btnPrimary"; + this.btnPrimary.Size = new System.Drawing.Size(120, 35); + this.btnPrimary.TabIndex = 1; + this.btnPrimary.Values.Text = "Primary"; + // + // btnSuccess + // + this.btnSuccess.Location = new System.Drawing.Point(285, 20); + this.btnSuccess.Name = "btnSuccess"; + this.btnSuccess.Size = new System.Drawing.Size(120, 35); + this.btnSuccess.TabIndex = 2; + this.btnSuccess.Values.Text = "Success"; + // + // grpCheckboxes + // + this.grpCheckboxes.Location = new System.Drawing.Point(15, 150); + this.grpCheckboxes.Name = "grpCheckboxes"; + this.grpCheckboxes.Size = new System.Drawing.Size(400, 100); + this.grpCheckboxes.TabIndex = 2; + this.grpCheckboxes.Values.Heading = "Checkboxes"; + // + // grpCheckboxes.Panel + // + this.grpCheckboxes.Panel.Controls.Add(this.chkOption3); + this.grpCheckboxes.Panel.Controls.Add(this.chkOption2); + this.grpCheckboxes.Panel.Controls.Add(this.chkOption1); + // + // chkOption1 + // + this.chkOption1.Location = new System.Drawing.Point(15, 20); + this.chkOption1.Name = "chkOption1"; + this.chkOption1.Size = new System.Drawing.Size(100, 20); + this.chkOption1.TabIndex = 0; + this.chkOption1.Values.Text = "Option 1"; + // + // chkOption2 + // + this.chkOption2.Location = new System.Drawing.Point(15, 50); + this.chkOption2.Name = "chkOption2"; + this.chkOption2.Size = new System.Drawing.Size(100, 20); + this.chkOption2.TabIndex = 1; + this.chkOption2.Values.Text = "Option 2"; + // + // chkOption3 + // + this.chkOption3.Location = new System.Drawing.Point(150, 20); + this.chkOption3.Name = "chkOption3"; + this.chkOption3.Size = new System.Drawing.Size(100, 20); + this.chkOption3.TabIndex = 2; + this.chkOption3.Values.Text = "Option 3"; + // + // grpRadioButtons + // + this.grpRadioButtons.Location = new System.Drawing.Point(430, 150); + this.grpRadioButtons.Name = "grpRadioButtons"; + this.grpRadioButtons.Size = new System.Drawing.Size(430, 100); + this.grpRadioButtons.TabIndex = 3; + this.grpRadioButtons.Values.Heading = "Radio Buttons"; + // + // grpRadioButtons.Panel + // + this.grpRadioButtons.Panel.Controls.Add(this.radioOption3); + this.grpRadioButtons.Panel.Controls.Add(this.radioOption2); + this.grpRadioButtons.Panel.Controls.Add(this.radioOption1); + // + // radioOption1 + // + this.radioOption1.Location = new System.Drawing.Point(15, 20); + this.radioOption1.Name = "radioOption1"; + this.radioOption1.Size = new System.Drawing.Size(120, 20); + this.radioOption1.TabIndex = 0; + this.radioOption1.Values.Text = "Radio Option A"; + // + // radioOption2 + // + this.radioOption2.Location = new System.Drawing.Point(15, 50); + this.radioOption2.Name = "radioOption2"; + this.radioOption2.Size = new System.Drawing.Size(120, 20); + this.radioOption2.TabIndex = 1; + this.radioOption2.Values.Text = "Radio Option B"; + // + // radioOption3 + // + this.radioOption3.Location = new System.Drawing.Point(150, 20); + this.radioOption3.Name = "radioOption3"; + this.radioOption3.Size = new System.Drawing.Size(120, 20); + this.radioOption3.TabIndex = 2; + this.radioOption3.Values.Text = "Radio Option C"; + // + // grpOtherControls + // + this.grpOtherControls.Location = new System.Drawing.Point(15, 265); + this.grpOtherControls.Name = "grpOtherControls"; + this.grpOtherControls.Size = new System.Drawing.Size(845, 100); + this.grpOtherControls.TabIndex = 4; + this.grpOtherControls.Values.Heading = "Other Controls"; + // + // grpOtherControls.Panel + // + this.grpOtherControls.Panel.Controls.Add(this.trackBar); + this.grpOtherControls.Panel.Controls.Add(this.progressBar); + // + // progressBar + // + this.progressBar.Location = new System.Drawing.Point(15, 20); + this.progressBar.Name = "progressBar"; + this.progressBar.Size = new System.Drawing.Size(400, 25); + this.progressBar.TabIndex = 0; + this.progressBar.Value = 65; + // + // trackBar + // + this.trackBar.Location = new System.Drawing.Point(15, 55); + this.trackBar.Name = "trackBar"; + this.trackBar.Size = new System.Drawing.Size(400, 45); + this.trackBar.TabIndex = 1; + this.trackBar.TickFrequency = 10; + // + // grpAdvancedControls + // + this.grpAdvancedControls.Location = new System.Drawing.Point(15, 380); + this.grpAdvancedControls.Name = "grpAdvancedControls"; + this.grpAdvancedControls.Size = new System.Drawing.Size(845, 100); + this.grpAdvancedControls.TabIndex = 5; + this.grpAdvancedControls.Values.Heading = "Advanced Controls (Navigator & Workspace - Per-Monitor DPI aware)"; + // + // grpAdvancedControls.Panel + // + this.grpAdvancedControls.Panel.Controls.Add(this.workspace); + this.grpAdvancedControls.Panel.Controls.Add(this.navigator); + // + // navigator + // + this.navigator.Bar.BarFirstItemInset = 0; + this.navigator.Bar.BarLastItemInset = 0; + this.navigator.Bar.BarMinimumHeight = 0; + this.navigator.Bar.TabStyle = Krypton.Toolkit.TabStyle.StandardProfile; + this.navigator.Button.ButtonDisplayLogic = Krypton.Navigator.ButtonDisplayLogic.None; + this.navigator.Button.ContextButtonAction = Krypton.Navigator.ContextButtonAction.None; + this.navigator.Dock = System.Windows.Forms.DockStyle.Left; + this.navigator.Location = new System.Drawing.Point(15, 20); + this.navigator.Name = "navigator"; + this.navigator.NavigatorMode = Krypton.Navigator.NavigatorMode.BarTabGroup; + this.navigator.Size = new System.Drawing.Size(250, 60); + this.navigator.TabIndex = 0; + // + // workspace + // + this.workspace.Dock = System.Windows.Forms.DockStyle.Fill; + this.workspace.Location = new System.Drawing.Point(265, 20); + this.workspace.Name = "workspace"; + this.workspace.Size = new System.Drawing.Size(565, 60); + this.workspace.TabIndex = 1; + // + // grpSettings + // + this.grpSettings.Dock = System.Windows.Forms.DockStyle.Bottom; + this.grpSettings.Location = new System.Drawing.Point(0, 500); + this.grpSettings.Name = "grpSettings"; + this.grpSettings.Size = new System.Drawing.Size(1000, 500); + this.grpSettings.TabIndex = 1; + this.grpSettings.Values.Heading = "Touchscreen + High DPI Settings"; + // + // grpSettings.Panel + // + this.grpSettings.Panel.Controls.Add(this.grpDpiInfo); + this.grpSettings.Panel.Controls.Add(this.lblStatus); + this.grpSettings.Panel.Controls.Add(this.btnToggle); + this.grpSettings.Panel.Controls.Add(this.btnApplyPreset75); + this.grpSettings.Panel.Controls.Add(this.btnApplyPreset50); + this.grpSettings.Panel.Controls.Add(this.btnApplyPreset25); + this.grpSettings.Panel.Controls.Add(this.btnResetScale); + this.grpSettings.Panel.Controls.Add(this.lblScaleValue); + this.grpSettings.Panel.Controls.Add(this.trackScaleFactor); + this.grpSettings.Panel.Controls.Add(this.lblScaleFactor); + this.grpSettings.Panel.Controls.Add(this.chkEnableTouchscreen); + this.grpSettings.Panel.Controls.Add(this.chkEnableFontScaling); + this.grpSettings.Panel.Controls.Add(this.lblFontScaleFactor); + this.grpSettings.Panel.Controls.Add(this.trackFontScaleFactor); + this.grpSettings.Panel.Controls.Add(this.lblFontScaleValue); + // + // chkEnableTouchscreen + // + this.chkEnableTouchscreen.Location = new System.Drawing.Point(15, 20); + this.chkEnableTouchscreen.Name = "chkEnableTouchscreen"; + this.chkEnableTouchscreen.Size = new System.Drawing.Size(200, 20); + this.chkEnableTouchscreen.TabIndex = 0; + this.chkEnableTouchscreen.Values.Text = "Enable Touchscreen Support"; + // + // lblScaleFactor + // + this.lblScaleFactor.Location = new System.Drawing.Point(15, 50); + this.lblScaleFactor.Name = "lblScaleFactor"; + this.lblScaleFactor.Size = new System.Drawing.Size(200, 20); + this.lblScaleFactor.TabIndex = 1; + this.lblScaleFactor.Values.Text = "Scale Factor (1.0x - 3.0x):"; + // + // trackScaleFactor + // + this.trackScaleFactor.Location = new System.Drawing.Point(15, 75); + this.trackScaleFactor.Maximum = 200; + this.trackScaleFactor.Minimum = 0; + this.trackScaleFactor.Name = "trackScaleFactor"; + this.trackScaleFactor.Size = new System.Drawing.Size(400, 45); + this.trackScaleFactor.TabIndex = 2; + this.trackScaleFactor.TickFrequency = 25; + this.trackScaleFactor.Value = 25; + // + // lblScaleValue + // + this.lblScaleValue.Location = new System.Drawing.Point(430, 75); + this.lblScaleValue.Name = "lblScaleValue"; + this.lblScaleValue.Size = new System.Drawing.Size(200, 20); + this.lblScaleValue.TabIndex = 3; + this.lblScaleValue.Values.Text = "1.25x (25.0% larger)"; + // + // btnResetScale + // + this.btnResetScale.Location = new System.Drawing.Point(650, 75); + this.btnResetScale.Name = "btnResetScale"; + this.btnResetScale.Size = new System.Drawing.Size(100, 35); + this.btnResetScale.TabIndex = 4; + this.btnResetScale.Values.Text = "Reset (1.25x)"; + // + // btnApplyPreset25 + // + this.btnApplyPreset25.Location = new System.Drawing.Point(15, 130); + this.btnApplyPreset25.Name = "btnApplyPreset25"; + this.btnApplyPreset25.Size = new System.Drawing.Size(120, 35); + this.btnApplyPreset25.TabIndex = 5; + this.btnApplyPreset25.Values.Text = "Preset: 25%"; + // + // btnApplyPreset50 + // + this.btnApplyPreset50.Location = new System.Drawing.Point(150, 130); + this.btnApplyPreset50.Name = "btnApplyPreset50"; + this.btnApplyPreset50.Size = new System.Drawing.Size(120, 35); + this.btnApplyPreset50.TabIndex = 6; + this.btnApplyPreset50.Values.Text = "Preset: 50%"; + // + // btnApplyPreset75 + // + this.btnApplyPreset75.Location = new System.Drawing.Point(285, 130); + this.btnApplyPreset75.Name = "btnApplyPreset75"; + this.btnApplyPreset75.Size = new System.Drawing.Size(120, 35); + this.btnApplyPreset75.TabIndex = 7; + this.btnApplyPreset75.Values.Text = "Preset: 75%"; + // + // btnToggle + // + this.btnToggle.Location = new System.Drawing.Point(430, 130); + this.btnToggle.Name = "btnToggle"; + this.btnToggle.Size = new System.Drawing.Size(200, 35); + this.btnToggle.TabIndex = 8; + this.btnToggle.Values.Text = "Toggle Support"; + // + // chkEnableFontScaling + // + this.chkEnableFontScaling.Location = new System.Drawing.Point(15, 180); + this.chkEnableFontScaling.Name = "chkEnableFontScaling"; + this.chkEnableFontScaling.Size = new System.Drawing.Size(200, 20); + this.chkEnableFontScaling.TabIndex = 9; + this.chkEnableFontScaling.Values.Text = "Enable Font Scaling"; + // + // lblFontScaleFactor + // + this.lblFontScaleFactor.Location = new System.Drawing.Point(15, 210); + this.lblFontScaleFactor.Name = "lblFontScaleFactor"; + this.lblFontScaleFactor.Size = new System.Drawing.Size(200, 20); + this.lblFontScaleFactor.TabIndex = 10; + this.lblFontScaleFactor.Values.Text = "Font Scale Factor (1.0x - 3.0x):"; + // + // trackFontScaleFactor + // + this.trackFontScaleFactor.Location = new System.Drawing.Point(15, 235); + this.trackFontScaleFactor.Maximum = 200; + this.trackFontScaleFactor.Minimum = 0; + this.trackFontScaleFactor.Name = "trackFontScaleFactor"; + this.trackFontScaleFactor.Size = new System.Drawing.Size(400, 45); + this.trackFontScaleFactor.TabIndex = 11; + this.trackFontScaleFactor.TickFrequency = 25; + this.trackFontScaleFactor.Value = 25; + // + // lblFontScaleValue + // + this.lblFontScaleValue.Location = new System.Drawing.Point(430, 235); + this.lblFontScaleValue.Name = "lblFontScaleValue"; + this.lblFontScaleValue.Size = new System.Drawing.Size(200, 20); + this.lblFontScaleValue.TabIndex = 12; + this.lblFontScaleValue.Values.Text = "1.25x (25.0% larger)"; + // + // grpDpiInfo + // + this.grpDpiInfo.Location = new System.Drawing.Point(15, 290); + this.grpDpiInfo.Name = "grpDpiInfo"; + this.grpDpiInfo.Size = new System.Drawing.Size(970, 190); + this.grpDpiInfo.TabIndex = 13; + this.grpDpiInfo.Values.Heading = "High DPI Information (Per-Monitor DPI Awareness)"; + // + // grpDpiInfo.Panel + // + this.grpDpiInfo.Panel.Controls.Add(this.lblScalingExample); + this.grpDpiInfo.Panel.Controls.Add(this.lblDpiWarning); + this.grpDpiInfo.Panel.Controls.Add(this.lblCombinedPerMonitor); + this.grpDpiInfo.Panel.Controls.Add(this.lblCombinedPrimary); + this.grpDpiInfo.Panel.Controls.Add(this.lblDpiActual); + this.grpDpiInfo.Panel.Controls.Add(this.lblDpiPerMonitor); + this.grpDpiInfo.Panel.Controls.Add(this.lblDpiPrimary); + this.grpDpiInfo.Panel.Controls.Add(this.btnRefreshDpi); + // + // btnRefreshDpi + // + this.btnRefreshDpi.Location = new System.Drawing.Point(850, 20); + this.btnRefreshDpi.Name = "btnRefreshDpi"; + this.btnRefreshDpi.Size = new System.Drawing.Size(100, 35); + this.btnRefreshDpi.TabIndex = 0; + this.btnRefreshDpi.Values.Text = "Refresh DPI"; + // + // lblDpiPrimary + // + this.lblDpiPrimary.Location = new System.Drawing.Point(15, 20); + this.lblDpiPrimary.Name = "lblDpiPrimary"; + this.lblDpiPrimary.Size = new System.Drawing.Size(400, 20); + this.lblDpiPrimary.TabIndex = 1; + this.lblDpiPrimary.Values.Text = "Primary Monitor DPI: 1.00x (1.00x, 1.00y)"; + // + // lblDpiPerMonitor + // + this.lblDpiPerMonitor.Location = new System.Drawing.Point(15, 50); + this.lblDpiPerMonitor.Name = "lblDpiPerMonitor"; + this.lblDpiPerMonitor.Size = new System.Drawing.Size(400, 20); + this.lblDpiPerMonitor.TabIndex = 2; + this.lblDpiPerMonitor.Values.Text = "Per-Monitor DPI: 1.00x (1.00x, 1.00y)"; + // + // lblDpiActual + // + this.lblDpiActual.Location = new System.Drawing.Point(15, 80); + this.lblDpiActual.Name = "lblDpiActual"; + this.lblDpiActual.Size = new System.Drawing.Size(400, 20); + this.lblDpiActual.TabIndex = 3; + this.lblDpiActual.Values.Text = "Windows API DPI: Not available"; + // + // lblCombinedPrimary + // + this.lblCombinedPrimary.Location = new System.Drawing.Point(430, 20); + this.lblCombinedPrimary.Name = "lblCombinedPrimary"; + this.lblCombinedPrimary.Size = new System.Drawing.Size(400, 20); + this.lblCombinedPrimary.TabIndex = 4; + this.lblCombinedPrimary.Values.Text = "Combined (Primary): 1.00x (1.00x, 1.00y)"; + // + // lblCombinedPerMonitor + // + this.lblCombinedPerMonitor.Location = new System.Drawing.Point(430, 50); + this.lblCombinedPerMonitor.Name = "lblCombinedPerMonitor"; + this.lblCombinedPerMonitor.Size = new System.Drawing.Size(400, 20); + this.lblCombinedPerMonitor.TabIndex = 5; + this.lblCombinedPerMonitor.Values.Text = "Combined (Per-Monitor): 1.00x (1.00x, 1.00y)"; + // + // lblDpiWarning + // + this.lblDpiWarning.Location = new System.Drawing.Point(15, 110); + this.lblDpiWarning.Name = "lblDpiWarning"; + this.lblDpiWarning.Size = new System.Drawing.Size(815, 20); + this.lblDpiWarning.TabIndex = 6; + this.lblDpiWarning.Values.Text = "✓ Single monitor or matching DPI"; + // + // lblScalingExample + // + this.lblScalingExample.Location = new System.Drawing.Point(15, 140); + this.lblScalingExample.Name = "lblScalingExample"; + this.lblScalingExample.Size = new System.Drawing.Size(815, 40); + this.lblScalingExample.TabIndex = 7; + this.lblScalingExample.Values.Text = "Scaling Example (base=100px): DPI only=100px, DPI+Touchscreen=100px, Per-Monitor=100px"; + // + // lblStatus + // + this.lblStatus.Location = new System.Drawing.Point(650, 20); + this.lblStatus.Name = "lblStatus"; + this.lblStatus.Size = new System.Drawing.Size(335, 50); + this.lblStatus.TabIndex = 14; + this.lblStatus.Values.Text = "Status: Disabled"; + // + // TouchscreenHighDpiDemo + // + this.AutoScaleDimensions = new System.Drawing.SizeF(7F, 15F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.ClientSize = new System.Drawing.Size(1000, 1000); + this.Controls.Add(this.grpControls); + this.Controls.Add(this.grpSettings); + this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.Sizable; + this.MinimumSize = new System.Drawing.Size(1020, 1020); + this.Name = "TouchscreenHighDpiDemo"; + this.Text = "Touchscreen + High DPI Scaling Demo (Issue #2844)"; + ((System.ComponentModel.ISupportInitialize)(this.grpControls)).EndInit(); + this.grpControls.Panel.ResumeLayout(false); + this.grpControls.ResumeLayout(false); + ((System.ComponentModel.ISupportInitialize)(this.grpInputControls)).EndInit(); + this.grpInputControls.Panel.ResumeLayout(false); + this.grpInputControls.Panel.PerformLayout(); + this.grpInputControls.ResumeLayout(false); + ((System.ComponentModel.ISupportInitialize)(this.grpButtons)).EndInit(); + this.grpButtons.Panel.ResumeLayout(false); + this.grpButtons.ResumeLayout(false); + ((System.ComponentModel.ISupportInitialize)(this.grpCheckboxes)).EndInit(); + this.grpCheckboxes.Panel.ResumeLayout(false); + this.grpCheckboxes.Panel.PerformLayout(); + this.grpCheckboxes.ResumeLayout(false); + ((System.ComponentModel.ISupportInitialize)(this.grpRadioButtons)).EndInit(); + this.grpRadioButtons.Panel.ResumeLayout(false); + this.grpRadioButtons.Panel.PerformLayout(); + this.grpRadioButtons.ResumeLayout(false); + ((System.ComponentModel.ISupportInitialize)(this.grpOtherControls)).EndInit(); + this.grpOtherControls.Panel.ResumeLayout(false); + this.grpOtherControls.Panel.PerformLayout(); + this.grpOtherControls.ResumeLayout(false); + ((System.ComponentModel.ISupportInitialize)(this.grpAdvancedControls)).EndInit(); + this.grpAdvancedControls.Panel.ResumeLayout(false); + this.grpAdvancedControls.ResumeLayout(false); + ((System.ComponentModel.ISupportInitialize)(this.grpSettings)).EndInit(); + this.grpSettings.Panel.ResumeLayout(false); + this.grpSettings.Panel.PerformLayout(); + this.grpSettings.ResumeLayout(false); + ((System.ComponentModel.ISupportInitialize)(this.grpDpiInfo)).EndInit(); + this.grpDpiInfo.Panel.ResumeLayout(false); + this.grpDpiInfo.Panel.PerformLayout(); + this.grpDpiInfo.ResumeLayout(false); + this.ResumeLayout(false); + + } + + #endregion + + private Krypton.Toolkit.KryptonGroupBox grpControls; + private Krypton.Toolkit.KryptonGroupBox grpAdvancedControls; + private Krypton.Navigator.KryptonNavigator navigator; + private Krypton.Workspace.KryptonWorkspace workspace; + private Krypton.Toolkit.KryptonGroupBox grpInputControls; + private Krypton.Toolkit.KryptonTextBox txtInput; + private Krypton.Toolkit.KryptonNumericUpDown txtNumeric; + private Krypton.Toolkit.KryptonComboBox cmbOptions; + private Krypton.Toolkit.KryptonGroupBox grpButtons; + private Krypton.Toolkit.KryptonButton btnStandard; + private Krypton.Toolkit.KryptonButton btnPrimary; + private Krypton.Toolkit.KryptonButton btnSuccess; + private Krypton.Toolkit.KryptonGroupBox grpCheckboxes; + private Krypton.Toolkit.KryptonCheckBox chkOption1; + private Krypton.Toolkit.KryptonCheckBox chkOption2; + private Krypton.Toolkit.KryptonCheckBox chkOption3; + private Krypton.Toolkit.KryptonGroupBox grpRadioButtons; + private Krypton.Toolkit.KryptonRadioButton radioOption1; + private Krypton.Toolkit.KryptonRadioButton radioOption2; + private Krypton.Toolkit.KryptonRadioButton radioOption3; + private Krypton.Toolkit.KryptonGroupBox grpOtherControls; + private Krypton.Toolkit.KryptonProgressBar progressBar; + private Krypton.Toolkit.KryptonTrackBar trackBar; + private Krypton.Toolkit.KryptonGroupBox grpSettings; + private Krypton.Toolkit.KryptonCheckBox chkEnableTouchscreen; + private Krypton.Toolkit.KryptonLabel lblScaleFactor; + private Krypton.Toolkit.KryptonTrackBar trackScaleFactor; + private Krypton.Toolkit.KryptonLabel lblScaleValue; + private Krypton.Toolkit.KryptonButton btnResetScale; + private Krypton.Toolkit.KryptonButton btnApplyPreset25; + private Krypton.Toolkit.KryptonButton btnApplyPreset50; + private Krypton.Toolkit.KryptonButton btnApplyPreset75; + private Krypton.Toolkit.KryptonButton btnToggle; + private Krypton.Toolkit.KryptonLabel lblStatus; + private Krypton.Toolkit.KryptonCheckBox chkEnableFontScaling; + private Krypton.Toolkit.KryptonLabel lblFontScaleFactor; + private Krypton.Toolkit.KryptonTrackBar trackFontScaleFactor; + private Krypton.Toolkit.KryptonLabel lblFontScaleValue; + private Krypton.Toolkit.KryptonGroupBox grpDpiInfo; + private Krypton.Toolkit.KryptonButton btnRefreshDpi; + private Krypton.Toolkit.KryptonLabel lblDpiPrimary; + private Krypton.Toolkit.KryptonLabel lblDpiPerMonitor; + private Krypton.Toolkit.KryptonLabel lblDpiActual; + private Krypton.Toolkit.KryptonLabel lblCombinedPrimary; + private Krypton.Toolkit.KryptonLabel lblCombinedPerMonitor; + private Krypton.Toolkit.KryptonLabel lblDpiWarning; + private Krypton.Toolkit.KryptonLabel lblScalingExample; + } +} \ No newline at end of file diff --git a/Source/Krypton Components/TestForm/TouchscreenHighDpiDemo.cs b/Source/Krypton Components/TestForm/TouchscreenHighDpiDemo.cs new file mode 100644 index 000000000..38ae62381 --- /dev/null +++ b/Source/Krypton Components/TestForm/TouchscreenHighDpiDemo.cs @@ -0,0 +1,479 @@ +#region BSD License +/* + * + * New BSD 3-Clause License (https://github.com/Krypton-Suite/Standard-Toolkit/blob/master/LICENSE) + * Modifications by Peter Wagner(aka Wagnerp) & Simon Coghlan(aka Smurf-IV), et al. 2026 - 2026. All rights reserved. + * + */ +#endregion + +using System.Runtime.InteropServices; + +using Krypton.Navigator; +using Krypton.Ribbon; +using Krypton.Toolkit; +using Krypton.Workspace; + +namespace TestForm; + +/// +/// Comprehensive demonstration of touchscreen support with high DPI scaling. +/// Shows per-monitor DPI awareness, combined scaling factors (DPI × Touchscreen), +/// and real-time monitoring of DPI changes when windows move between monitors. +/// +public partial class TouchscreenHighDpiDemo : KryptonForm +{ + private Timer _dpiMonitorTimer; + private bool _updatingFromEvent; + + public TouchscreenHighDpiDemo() + { + InitializeComponent(); + InitializeForm(); + } + + private void InitializeForm() + { + // Subscribe to touchscreen support changes + KryptonManager.GlobalTouchscreenSupportChanged += OnGlobalTouchscreenSupportChanged; + + // Setup DPI monitoring timer + _dpiMonitorTimer = new Timer { Interval = 500 }; + _dpiMonitorTimer.Tick += DpiMonitorTimer_Tick; + _dpiMonitorTimer.Start(); + + // Initialize UI with current settings + UpdateUIFromSettings(); + + // Setup demo controls + SetupDemoControls(); + + // Setup event handlers + chkEnableTouchscreen.CheckedChanged += ChkEnableTouchscreen_CheckedChanged; + trackScaleFactor.ValueChanged += TrackScaleFactor_ValueChanged; + chkEnableFontScaling.CheckedChanged += ChkEnableFontScaling_CheckedChanged; + trackFontScaleFactor.ValueChanged += TrackFontScaleFactor_ValueChanged; + btnResetScale.Click += BtnResetScale_Click; + btnApplyPreset25.Click += BtnApplyPreset25_Click; + btnApplyPreset50.Click += BtnApplyPreset50_Click; + btnApplyPreset75.Click += BtnApplyPreset75_Click; + btnToggle.Click += BtnToggle_Click; + btnRefreshDpi.Click += BtnRefreshDpi_Click; + + // Handle form move/resize to detect monitor changes + this.Move += (s, e) => UpdateDpiInfo(); + this.Resize += (s, e) => UpdateDpiInfo(); + this.DpiChanged += (s, e) => OnDpiChanged(e); + + // Update status + UpdateStatus(); + UpdateDpiInfo(); + } + + private void SetupDemoControls() + { + // Button examples + btnStandard.Text = "Standard Button"; + btnStandard.Click += (s, e) => KryptonMessageBox.Show("Standard button clicked!", "High DPI + Touchscreen Demo"); + + btnPrimary.Text = "Primary Button"; + btnPrimary.ButtonStyle = ButtonStyle.Command; + btnPrimary.Click += (s, e) => KryptonMessageBox.Show("Primary button clicked!", "High DPI + Touchscreen Demo"); + + btnSuccess.Text = "Success Button"; + btnSuccess.StateCommon.Content.ShortText.Color1 = Color.Green; + btnSuccess.Click += (s, e) => KryptonMessageBox.Show("Success button clicked!", "High DPI + Touchscreen Demo"); + + // Checkbox examples + chkOption1.Text = "Option 1"; + chkOption2.Text = "Option 2"; + chkOption3.Text = "Option 3"; + + // Radio button examples + radioOption1.Text = "Radio Option A"; + radioOption2.Text = "Radio Option B"; + radioOption3.Text = "Radio Option C"; + radioOption1.Checked = true; + + // Text input examples + txtInput.Text = "Sample text input"; + txtInput.CueHint.CueHintText = "Enter text here..."; + txtNumeric.Value = 42; + + // ComboBox example + cmbOptions.Items.AddRange(new[] { "Option 1", "Option 2", "Option 3", "Option 4" }); + cmbOptions.SelectedIndex = 0; + + // Progress bar + progressBar.Value = 65; + progressBar.StateCommon.Content.LongText.Color1 = Color.Blue; + + // Track bar + trackBar.Minimum = 0; + trackBar.Maximum = 100; + trackBar.Value = 50; + trackBar.TickFrequency = 10; + + // Navigator example - Create pages first + var page1 = new KryptonPage { Text = "Page 1", TextTitle = "First Page" }; + var label1 = new KryptonLabel + { + Text = "Navigator Page 1 - High DPI + Touchscreen scaling applies to tabs and buttons", + Dock = DockStyle.Fill + }; + label1.StateCommon.ShortText.TextH = PaletteRelativeAlign.Center; + label1.StateCommon.ShortText.TextV = PaletteRelativeAlign.Center; + page1.Controls.Add(label1); + navigator.Pages.Add(page1); + + var page2 = new KryptonPage { Text = "Page 2", TextTitle = "Second Page" }; + var label2 = new KryptonLabel + { + Text = "Navigator Page 2 - All controls scale when touchscreen support is enabled on high DPI displays", + Dock = DockStyle.Fill + }; + label2.StateCommon.ShortText.TextH = PaletteRelativeAlign.Center; + label2.StateCommon.ShortText.TextV = PaletteRelativeAlign.Center; + page2.Controls.Add(label2); + navigator.Pages.Add(page2); + + navigator.SelectedPage = page1; + + // Workspace example - Create cells with pages + var cell1 = new KryptonWorkspaceCell(); + var workspacePage1 = new KryptonPage { Text = "Workspace Cell 1", TextTitle = "Cell 1" }; + var label1w = new KryptonLabel + { + Text = "Workspace Cell 1 - Workspace cells and their tabs scale with touchscreen support on high DPI", + Dock = DockStyle.Fill + }; + label1w.StateCommon.ShortText.TextH = PaletteRelativeAlign.Center; + label1w.StateCommon.ShortText.TextV = PaletteRelativeAlign.Center; + workspacePage1.Controls.Add(label1w); + cell1.Pages.Add(workspacePage1); + cell1.SelectedPage = workspacePage1; + + var cell2 = new KryptonWorkspaceCell(); + var workspacePage2 = new KryptonPage { Text = "Workspace Cell 2", TextTitle = "Cell 2" }; + var label2w = new KryptonLabel + { + Text = "Workspace Cell 2 - Navigator, Ribbon, Workspace, and Docking all support touchscreen scaling with per-monitor DPI", + Dock = DockStyle.Fill + }; + label2w.StateCommon.ShortText.TextH = PaletteRelativeAlign.Center; + label2w.StateCommon.ShortText.TextV = PaletteRelativeAlign.Center; + workspacePage2.Controls.Add(label2w); + cell2.Pages.Add(workspacePage2); + cell2.SelectedPage = workspacePage2; + + workspace.Root.Children.Add(cell1); + workspace.Root.Children.Add(cell2); + } + + private void ChkEnableTouchscreen_CheckedChanged(object? sender, EventArgs e) + { + if (_updatingFromEvent) return; + + try + { + KryptonManager.TouchscreenSettingsValues.TouchscreenModeEnabled = chkEnableTouchscreen.Checked; + UpdateStatus(); + UpdateDpiInfo(); + } + catch (Exception ex) + { + KryptonMessageBox.Show($"Error: {ex.Message}", "Touchscreen Support", KryptonMessageBoxButtons.OK, KryptonMessageBoxIcon.Error); + } + } + + private void TrackScaleFactor_ValueChanged(object? sender, EventArgs e) + { + if (_updatingFromEvent) return; + + try + { + float scaleFactor = 1.0f + (trackScaleFactor.Value / 100f); + KryptonManager.TouchscreenSettingsValues.ControlScaleFactor = scaleFactor; + lblScaleValue.Text = $"{scaleFactor:F2}x ({(scaleFactor * 100 - 100):F1}% larger)"; + UpdateStatus(); + UpdateDpiInfo(); + } + catch (Exception ex) + { + KryptonMessageBox.Show($"Error: {ex.Message}", "Touchscreen Support", KryptonMessageBoxButtons.OK, KryptonMessageBoxIcon.Error); + } + } + + private void ChkEnableFontScaling_CheckedChanged(object? sender, EventArgs e) + { + if (_updatingFromEvent) return; + + try + { + KryptonManager.TouchscreenSettingsValues.FontScalingEnabled = chkEnableFontScaling.Checked; + trackFontScaleFactor.Enabled = KryptonManager.TouchscreenSettingsValues.TouchscreenModeEnabled && chkEnableFontScaling.Checked; + UpdateStatus(); + } + catch (Exception ex) + { + KryptonMessageBox.Show($"Error: {ex.Message}", "Touchscreen Support", KryptonMessageBoxButtons.OK, KryptonMessageBoxIcon.Error); + } + } + + private void TrackFontScaleFactor_ValueChanged(object? sender, EventArgs e) + { + if (_updatingFromEvent) return; + + try + { + float scaleFactor = 1.0f + (trackFontScaleFactor.Value / 100f); + KryptonManager.TouchscreenSettingsValues.FontScaleFactor = scaleFactor; + lblFontScaleValue.Text = $"{scaleFactor:F2}x ({(scaleFactor * 100 - 100):F1}% larger)"; + UpdateStatus(); + } + catch (Exception ex) + { + KryptonMessageBox.Show($"Error: {ex.Message}", "Touchscreen Support", KryptonMessageBoxButtons.OK, KryptonMessageBoxIcon.Error); + } + } + + private void BtnResetScale_Click(object? sender, EventArgs e) + { + try + { + KryptonManager.TouchscreenSettingsValues.ControlScaleFactor = 1.25f; // Default 25% larger + KryptonManager.TouchscreenSettingsValues.FontScaleFactor = 1.25f; // Default 25% larger + UpdateUIFromSettings(); + UpdateStatus(); + UpdateDpiInfo(); + } + catch (Exception ex) + { + KryptonMessageBox.Show($"Error: {ex.Message}", "Touchscreen Support", KryptonMessageBoxButtons.OK, KryptonMessageBoxIcon.Error); + } + } + + private void BtnApplyPreset25_Click(object? sender, EventArgs e) + { + ApplyPreset(1.25f, "25% larger (1.25x)"); + } + + private void BtnApplyPreset50_Click(object? sender, EventArgs e) + { + ApplyPreset(1.50f, "50% larger (1.50x)"); + } + + private void BtnApplyPreset75_Click(object? sender, EventArgs e) + { + ApplyPreset(1.75f, "75% larger (1.75x)"); + } + + private void ApplyPreset(float scaleFactor, string description) + { + try + { + KryptonManager.TouchscreenSettingsValues.TouchscreenModeEnabled = true; + KryptonManager.TouchscreenSettingsValues.ControlScaleFactor = scaleFactor; + KryptonManager.TouchscreenSettingsValues.FontScaleFactor = scaleFactor; // Match font scale to control scale + UpdateUIFromSettings(); + UpdateStatus(); + UpdateDpiInfo(); + KryptonMessageBox.Show($"Applied preset: {description}", "Touchscreen Support", KryptonMessageBoxButtons.OK, KryptonMessageBoxIcon.Information); + } + catch (Exception ex) + { + KryptonMessageBox.Show($"Error: {ex.Message}", "Touchscreen Support", KryptonMessageBoxButtons.OK, KryptonMessageBoxIcon.Error); + } + } + + private void BtnToggle_Click(object? sender, EventArgs e) + { + try + { + KryptonManager.TouchscreenSettingsValues.TouchscreenModeEnabled = !KryptonManager.UseTouchscreenSupport; + UpdateUIFromSettings(); + UpdateStatus(); + UpdateDpiInfo(); + } + catch (Exception ex) + { + KryptonMessageBox.Show($"Error: {ex.Message}", "Touchscreen Support", KryptonMessageBoxButtons.OK, KryptonMessageBoxIcon.Error); + } + } + + private void BtnRefreshDpi_Click(object? sender, EventArgs e) + { + // Invalidate DPI cache and refresh + KryptonManager.InvalidateDpiCache(); + UpdateDpiInfo(); + } + + private void OnDpiChanged(DpiChangedEventArgs e) + { + // DPI changed - invalidate cache and update info + KryptonManager.InvalidateDpiCache(); + UpdateDpiInfo(); + UpdateStatus(); + } + + private void DpiMonitorTimer_Tick(object? sender, EventArgs e) + { + // Periodically update DPI info to catch monitor changes + UpdateDpiInfo(); + } + + private void OnGlobalTouchscreenSupportChanged(object? sender, EventArgs e) + { + // Update UI when settings change externally + if (InvokeRequired) + { + Invoke(new Action(UpdateUIFromSettings)); + Invoke(new Action(UpdateStatus)); + Invoke(new Action(UpdateDpiInfo)); + } + else + { + UpdateUIFromSettings(); + UpdateStatus(); + UpdateDpiInfo(); + } + } + + private void UpdateUIFromSettings() + { + _updatingFromEvent = true; + try + { + var settings = KryptonManager.TouchscreenSettingsValues; + + chkEnableTouchscreen.Checked = settings.TouchscreenModeEnabled; + + // Convert control scale factor (1.0 - 3.0) to trackbar value (0-200) + float controlScaleFactor = settings.ControlScaleFactor; + int trackValue = (int)Math.Round((controlScaleFactor - 1.0f) * 100f); + trackValue = Math.Max(0, Math.Min(200, trackValue)); // Clamp to valid range + trackScaleFactor.Value = trackValue; + lblScaleValue.Text = $"{controlScaleFactor:F2}x ({(controlScaleFactor * 100 - 100):F1}% larger)"; + + // Font scaling controls + chkEnableFontScaling.Checked = settings.FontScalingEnabled; + + // Convert font scale factor (1.0 - 3.0) to trackbar value (0-200) + float fontScaleFactor = settings.FontScaleFactor; + int fontTrackValue = (int)Math.Round((fontScaleFactor - 1.0f) * 100f); + fontTrackValue = Math.Max(0, Math.Min(200, fontTrackValue)); // Clamp to valid range + trackFontScaleFactor.Value = fontTrackValue; + lblFontScaleValue.Text = $"{fontScaleFactor:F2}x ({(fontScaleFactor * 100 - 100):F1}% larger)"; + + // Enable/disable font scaling controls based on touchscreen support + bool touchscreenEnabled = settings.TouchscreenModeEnabled; + chkEnableFontScaling.Enabled = touchscreenEnabled; + bool fontScalingEnabled = touchscreenEnabled && settings.FontScalingEnabled; + trackFontScaleFactor.Enabled = fontScalingEnabled; + lblFontScaleFactor.Enabled = touchscreenEnabled; + lblFontScaleValue.Enabled = touchscreenEnabled; + } + finally + { + _updatingFromEvent = false; + } + } + + private void UpdateStatus() + { + var settings = KryptonManager.TouchscreenSettingsValues; + bool isEnabled = settings.TouchscreenModeEnabled; + float controlScaleFactor = KryptonManager.TouchscreenScaleFactor; + bool fontScalingEnabled = settings.FontScalingEnabled && isEnabled; + float fontScaleFactor = KryptonManager.TouchscreenFontScaleFactor; + + string statusText; + if (isEnabled) + { + statusText = $"Touchscreen Support: ENABLED - Control Scale: {controlScaleFactor:F2}x ({(controlScaleFactor * 100 - 100):F1}% larger)"; + if (fontScalingEnabled) + { + statusText += $" | Font Scale: {fontScaleFactor:F2}x ({(fontScaleFactor * 100 - 100):F1}% larger)"; + } + else + { + statusText += " | Font Scaling: DISABLED"; + } + } + else + { + statusText = $"Touchscreen Support: DISABLED - Controls at normal size"; + } + + lblStatus.Text = statusText; + lblStatus.StateCommon.ShortText.Color1 = isEnabled ? Color.Green : Color.Gray; + + // Update button text + btnToggle.Text = isEnabled ? "Disable Touchscreen Support" : "Enable Touchscreen Support"; + } + + private void UpdateDpiInfo() + { + if (!IsHandleCreated) return; + + IntPtr hWnd = Handle; + + // Get primary monitor DPI (legacy method) + float dpiXPrimary = KryptonManager.GetDpiFactorX(); + float dpiYPrimary = KryptonManager.GetDpiFactorY(); + float dpiAvgPrimary = KryptonManager.GetDpiFactor(); + + // Get per-monitor DPI (window-aware method) + float dpiXPerMonitor = KryptonManager.GetDpiFactorX(hWnd); + float dpiYPerMonitor = KryptonManager.GetDpiFactorY(hWnd); + float dpiAvgPerMonitor = KryptonManager.GetDpiFactor(hWnd); + + // Get combined scaling factors (DPI × Touchscreen) + float combinedXPrimary = KryptonManager.GetCombinedScaleFactorX(); + float combinedYPrimary = KryptonManager.GetCombinedScaleFactorY(); + float combinedAvgPrimary = KryptonManager.GetCombinedScaleFactor(); + + float combinedXPerMonitor = KryptonManager.GetCombinedScaleFactorX(hWnd); + float combinedYPerMonitor = KryptonManager.GetCombinedScaleFactorY(hWnd); + float combinedAvgPerMonitor = KryptonManager.GetCombinedScaleFactor(hWnd); + + // Calculate actual DPI value from the per-monitor factor (DPI = factor * 96) + int actualDpi = (int)Math.Round(dpiAvgPerMonitor * 96f); + + // Update DPI info labels + lblDpiPrimary.Text = $"Primary Monitor DPI: {dpiAvgPrimary:F2}x ({dpiXPrimary:F2}x, {dpiYPrimary:F2}y)"; + lblDpiPerMonitor.Text = $"Per-Monitor DPI: {dpiAvgPerMonitor:F2}x ({dpiXPerMonitor:F2}x, {dpiYPerMonitor:F2}y)"; + + // Display the calculated DPI value + lblDpiActual.Text = $"Calculated DPI: {actualDpi} ({dpiAvgPerMonitor:F2}x)"; + lblDpiActual.StateCommon.ShortText.Color1 = Color.Blue; + + // Update combined scaling info + lblCombinedPrimary.Text = $"Combined (Primary): {combinedAvgPrimary:F2}x ({combinedXPrimary:F2}x, {combinedYPrimary:F2}y)"; + lblCombinedPerMonitor.Text = $"Combined (Per-Monitor): {combinedAvgPerMonitor:F2}x ({combinedXPerMonitor:F2}x, {combinedYPerMonitor:F2}y)"; + + // Highlight if there's a difference (multi-monitor scenario) + if (Math.Abs(dpiAvgPrimary - dpiAvgPerMonitor) > 0.01f) + { + lblDpiPerMonitor.StateCommon.ShortText.Color1 = Color.Orange; + lblCombinedPerMonitor.StateCommon.ShortText.Color1 = Color.Orange; + lblDpiWarning.Text = "⚠ Multi-monitor detected: Per-monitor DPI differs from primary monitor"; + lblDpiWarning.StateCommon.ShortText.Color1 = Color.Orange; + } + else + { + lblDpiPerMonitor.StateCommon.ShortText.Color1 = Color.Black; + lblCombinedPerMonitor.StateCommon.ShortText.Color1 = Color.Black; + lblDpiWarning.Text = "✓ Single monitor or matching DPI"; + lblDpiWarning.StateCommon.ShortText.Color1 = Color.Green; + } + + // Show scaling example + int baseSize = 100; + int scaledByDpi = KryptonManager.ScaleValueByDpi(baseSize); + int scaledByBoth = KryptonManager.ScaleValueByDpiAndTouchscreen(baseSize); + int scaledByBothPerMonitor = (int)Math.Round(baseSize * combinedAvgPerMonitor); + + lblScalingExample.Text = $"Scaling Example (base={baseSize}px): DPI only={scaledByDpi}px, DPI+Touchscreen={scaledByBoth}px, Per-Monitor={scaledByBothPerMonitor}px"; + } +} diff --git a/Source/Krypton Components/TestForm/TouchscreenSupportTest.cs b/Source/Krypton Components/TestForm/TouchscreenSupportTest.cs index a5f2e795d..d7581050c 100644 --- a/Source/Krypton Components/TestForm/TouchscreenSupportTest.cs +++ b/Source/Krypton Components/TestForm/TouchscreenSupportTest.cs @@ -55,6 +55,11 @@ private void InitializeForm() // Update status UpdateStatus(); + + // Add DPI cache invalidation on form resize/move to handle monitor changes + Resize += (s, e) => KryptonManager.InvalidateDpiCache(); + + Move += (s, e) => KryptonManager.InvalidateDpiCache(); } private void SetupDemoControls() @@ -370,6 +375,14 @@ private void UpdateStatus() bool autoDetect = settings.AutomaticallyDetectTouchscreen; bool isAvailable = KryptonManager.IsTouchscreenAvailable(); + // Get DPI information + float dpiX = KryptonManager.GetDpiFactorX(); + float dpiY = KryptonManager.GetDpiFactorY(); + float dpiAvg = KryptonManager.GetDpiFactor(); + float combinedX = KryptonManager.GetCombinedScaleFactorX(); + float combinedY = KryptonManager.GetCombinedScaleFactorY(); + float combinedAvg = KryptonManager.GetCombinedScaleFactor(); + string statusText; if (isEnabled) { @@ -382,10 +395,13 @@ private void UpdateStatus() { statusText += " | Font Scaling: DISABLED"; } + + // Add DPI and combined scaling info + statusText += $" | DPI: {dpiAvg:F2}x ({dpiX:F2}x, {dpiY:F2}y) | Combined: {combinedAvg:F2}x ({combinedX:F2}x, {combinedY:F2}y)"; } else { - statusText = "Touchscreen Support: DISABLED - Controls at normal size"; + statusText = $"Touchscreen Support: DISABLED - Controls at normal size | DPI: {dpiAvg:F2}x ({dpiX:F2}x, {dpiY:F2}y)"; } if (autoDetect) @@ -405,6 +421,59 @@ private void UpdateStatus() btnToggle.Text = isEnabled ? "Disable Touchscreen Support" : "Enable Touchscreen Support"; } + /// + /// Demonstrates the DPI-aware helper methods available in KryptonManager. + /// This method shows examples of how to use the scaling helpers for various types. + /// + private void DemonstrateDpiHelperMethods() + { + // Example 1: Get DPI factors + float dpiX = KryptonManager.GetDpiFactorX(); + float dpiY = KryptonManager.GetDpiFactorY(); + float dpiAvg = KryptonManager.GetDpiFactor(); + + // Example 2: Get combined scaling (DPI × Touchscreen) + float combinedX = KryptonManager.GetCombinedScaleFactorX(); + float combinedY = KryptonManager.GetCombinedScaleFactorY(); + float combinedAvg = KryptonManager.GetCombinedScaleFactor(); + + // Example 3: Scale a single value by DPI + int baseSize = 100; + int scaledByDpi = KryptonManager.ScaleValueByDpi(baseSize); + float scaledByDpiFloat = KryptonManager.ScaleValueByDpi(100f); + + // Example 4: Scale a value by combined DPI and touchscreen + int scaledByBoth = KryptonManager.ScaleValueByDpiAndTouchscreen(baseSize); + float scaledByBothFloat = KryptonManager.ScaleValueByDpiAndTouchscreen(100f); + + // Example 5: Scale a Size + Size baseSizeObj = new Size(200, 100); + Size scaledByDpiSize = KryptonManager.ScaleSizeByDpi(baseSizeObj); + Size scaledByBothSize = KryptonManager.ScaleSizeByDpiAndTouchscreen(baseSizeObj); + + // Example 6: Scale a Point + Point basePoint = new Point(50, 75); + Point scaledByDpiPoint = KryptonManager.ScalePointByDpi(basePoint); + Point scaledByBothPoint = KryptonManager.ScalePointByDpiAndTouchscreen(basePoint); + + // Example 7: Scale a Rectangle + Rectangle baseRect = new Rectangle(10, 20, 200, 100); + Rectangle scaledByDpiRect = KryptonManager.ScaleRectangleByDpi(baseRect); + Rectangle scaledByBothRect = KryptonManager.ScaleRectangleByDpiAndTouchscreen(baseRect); + + // Example 8: Invalidate DPI cache when moving to a different monitor + // KryptonManager.InvalidateDpiCache(); // Call this when DPI changes + + // Display results (for demonstration purposes) + string message = $"DPI Helper Methods Demo:\n\n" + + $"DPI Factors: X={dpiX:F2}, Y={dpiY:F2}, Avg={dpiAvg:F2}\n" + + $"Combined Factors: X={combinedX:F2}, Y={combinedY:F2}, Avg={combinedAvg:F2}\n\n" + + $"Base Size: {baseSize} → Scaled by DPI: {scaledByDpi}, Scaled by Both: {scaledByBoth}\n" + + $"Base Size Object: {baseSizeObj} → Scaled by DPI: {scaledByDpiSize}, Scaled by Both: {scaledByBothSize}\n" + + $"Base Point: {basePoint} → Scaled by DPI: {scaledByDpiPoint}, Scaled by Both: {scaledByBothPoint}\n" + + $"Base Rectangle: {baseRect} → Scaled by DPI: {scaledByDpiRect}, Scaled by Both: {scaledByBothRect}"; + + KryptonMessageBox.Show(message, "DPI Helper Methods Demo", KryptonMessageBoxButtons.OK, KryptonMessageBoxIcon.Information); private void ChkAutoDetect_CheckedChanged(object? sender, EventArgs e) { if (_updatingFromEvent) return;