From d5be1fd9713ec295da66bcf10e42c13e3ff5748e Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Mon, 8 Jun 2026 19:52:20 +0000 Subject: [PATCH 1/4] chore(ci): sync .github from master into alpha --- .github/workflows/build-testform.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build-testform.yml b/.github/workflows/build-testform.yml index 6561cf4946..4c03836db5 100644 --- a/.github/workflows/build-testform.yml +++ b/.github/workflows/build-testform.yml @@ -86,7 +86,7 @@ jobs: ${{ runner.os }}-nuget- - name: Restore TestForm - run: dotnet restore "Source/Krypton Components/TestForm/TestForm.csproj" -p:Configuration=Release -p:TFMs=all + run: dotnet restore "Source/Krypton Components/TestForm/TestForm.csproj" -p:Configuration=Release -p:TFMs=all -p:ExcludeNet11=true - name: Validate TestForm linked resources shell: pwsh @@ -137,7 +137,7 @@ jobs: } - name: Build TestForm - run: dotnet build "Source/Krypton Components/TestForm/TestForm.csproj" -p:Configuration=Release --no-restore -p:TFMs=all -m:1 -p:BuildInParallel=false + run: dotnet build "Source/Krypton Components/TestForm/TestForm.csproj" -p:Configuration=Release --no-restore -p:TFMs=all -p:ExcludeNet11=true -m:1 -p:BuildInParallel=false - name: Save NuGet cache if: success() && github.event_name != 'pull_request' From bdcd9cb4e5f230863aba80efd6e44c5e19b9c0e5 Mon Sep 17 00:00:00 2001 From: tobitege <10787084+tobitege@users.noreply.github.com> Date: Tue, 9 Jun 2026 01:09:11 +0200 Subject: [PATCH 2/4] Fix themed DataGridView scrollbar interactions (#3682) --- .../Controls Toolkit/KryptonDataGridView.cs | 92 +++++++------------ 1 file changed, 31 insertions(+), 61 deletions(-) diff --git a/Source/Krypton Components/Krypton.Toolkit/Controls Toolkit/KryptonDataGridView.cs b/Source/Krypton Components/Krypton.Toolkit/Controls Toolkit/KryptonDataGridView.cs index 2fd3c0c282..22f4e01e25 100644 --- a/Source/Krypton Components/Krypton.Toolkit/Controls Toolkit/KryptonDataGridView.cs +++ b/Source/Krypton Components/Krypton.Toolkit/Controls Toolkit/KryptonDataGridView.cs @@ -1155,10 +1155,7 @@ protected override void OnScroll(ScrollEventArgs e) { base.OnScroll(e); - if (_roundingUsesDetachedScrollbars && !_suppressRoundingScrollSync && _roundingScrollBarInteractionDepth == 0) - { - SyncDetachedRoundingScrollbarsFromGrid(false); - } + SyncDetachedRoundingScrollbarsFromGridIfIdle(false); // #2681 - work-around // Headers not correctly repainted on horizontal mouse scroll @@ -1333,7 +1330,7 @@ or KryptonDataGridViewDomainUpDownCell // the painting to fail. if ((_cellDown.X == -1) || (_cellDown.Y == -1)) { - DoubleBuffered = false; + base.DoubleBuffered = false; } base.OnCellMouseDown(e); @@ -1358,9 +1355,9 @@ protected override void OnCellMouseUp(DataGridViewCellMouseEventArgs e) _cellDown = _nullCell; // Put back double buffered if it was turned off in the OnCellMouseDown - if (!DoubleBuffered) + if (!base.DoubleBuffered) { - DoubleBuffered = true; + base.DoubleBuffered = true; } base.OnCellMouseUp(e); @@ -2981,24 +2978,18 @@ private void SyncDetachedRoundingScrollbarsFromGrid(bool layoutScrollbars) return; } - var hScrollInfo = new WIN32ScrollBars.ScrollInfo - { - cbSize = Marshal.SizeOf(typeof(WIN32ScrollBars.ScrollInfo)), - fMask = (int)PI.SIF_.ALL - }; var vScrollInfo = new WIN32ScrollBars.ScrollInfo { cbSize = Marshal.SizeOf(typeof(WIN32ScrollBars.ScrollInfo)), fMask = (int)PI.SIF_.ALL }; - bool hasHScroll = PI.GetScrollInfo(Handle, PI.SB_.HORZ, ref hScrollInfo); bool hasVScroll = PI.GetScrollInfo(Handle, PI.SB_.VERT, ref vScrollInfo); _suppressRoundingScrollSync = true; try { - UpdateDetachedHorizontalScrollBar(hasHScroll, hScrollInfo); + UpdateDetachedHorizontalScrollBar(); UpdateDetachedVerticalScrollBar(hasVScroll, vScrollInfo); } finally @@ -3009,11 +3000,19 @@ private void SyncDetachedRoundingScrollbarsFromGrid(bool layoutScrollbars) LayoutDetachedRoundingScrollbars(layoutScrollbars); } - private void UpdateDetachedHorizontalScrollBar(bool hasNativeScrollInfo, WIN32ScrollBars.ScrollInfo scrollInfo) + private void SyncDetachedRoundingScrollbarsFromGridIfIdle(bool layoutScrollbars) + { + if (_roundingUsesDetachedScrollbars && !_suppressRoundingScrollSync && _roundingScrollBarInteractionDepth == 0) + { + SyncDetachedRoundingScrollbarsFromGrid(layoutScrollbars); + } + } + + private void UpdateDetachedHorizontalScrollBar() { if (_roundingHScrollBar == null || !WantsDetachedHorizontalScrollBar()) { - if (_roundingHScrollBar != null) + if (_roundingHScrollBar != null && _roundingHScrollBar.Visible) { _roundingHScrollBar.Visible = false; } @@ -3021,29 +3020,17 @@ private void UpdateDetachedHorizontalScrollBar(bool hasNativeScrollInfo, WIN32Sc return; } - SetDetachedHorizontalScrollBarBounds(false); - int minimum; int maximum; int page; int position; - if (TryGetNativeDataGridScrollBarMetrics(true, out minimum, out maximum, out page, out position)) - { - ApplyDetachedScrollBarMetrics(_roundingHScrollBar, minimum, maximum, page, position); - return; - } - - if (hasNativeScrollInfo && TryGetNativeScrollMetrics(scrollInfo, out minimum, out maximum, out page, out position)) + if (TryComputeHorizontalScrollMetrics(out minimum, out maximum, out page, out position)) { ApplyDetachedScrollBarMetrics(_roundingHScrollBar, minimum, maximum, page, position); return; } - if (TryComputeHorizontalScrollMetrics(out minimum, out maximum, out page, out position)) - { - ApplyDetachedScrollBarMetrics(_roundingHScrollBar, minimum, maximum, page, position); - } - else + if (_roundingHScrollBar.Visible) { _roundingHScrollBar.Visible = false; } @@ -3053,7 +3040,7 @@ private void UpdateDetachedVerticalScrollBar(bool hasNativeScrollInfo, WIN32Scro { if (_roundingVScrollBar == null || !WantsDetachedVerticalScrollBar()) { - if (_roundingVScrollBar != null) + if (_roundingVScrollBar != null && _roundingVScrollBar.Visible) { _roundingVScrollBar.Visible = false; } @@ -3061,8 +3048,6 @@ private void UpdateDetachedVerticalScrollBar(bool hasNativeScrollInfo, WIN32Scro return; } - SetDetachedVerticalScrollBarBounds(false); - int minimum; int maximum; int page; @@ -3085,7 +3070,10 @@ private void UpdateDetachedVerticalScrollBar(bool hasNativeScrollInfo, WIN32Scro } else { - _roundingVScrollBar.Visible = false; + if (_roundingVScrollBar.Visible) + { + _roundingVScrollBar.Visible = false; + } } } @@ -3339,33 +3327,7 @@ private int GetVerticalScrollPositionInPixels() private int GetHorizontalScrollPositionInPixels() { - var hScrollInfo = new WIN32ScrollBars.ScrollInfo - { - cbSize = Marshal.SizeOf(typeof(WIN32ScrollBars.ScrollInfo)), - fMask = (int)PI.SIF_.POS - }; - - if (IsHandleCreated && PI.GetScrollInfo(Handle, PI.SB_.HORZ, ref hScrollInfo)) - { - return hScrollInfo.nPos; - } - - int position = 0; - int firstColumn = Math.Max(0, FirstDisplayedScrollingColumnIndex); - if (RowHeadersVisible) - { - position += RowHeadersWidth; - } - - for (int i = 0; i < firstColumn && i < Columns.Count; i++) - { - if (Columns[i].Visible) - { - position += Columns[i].Width; - } - } - - return position; + return HorizontalScrollingOffset; } private static int GetNativeScrollableMaximum(int minimum, int maximum, int page) => @@ -4093,6 +4055,14 @@ protected override void WndProc(ref Message m) } #endregion menus + /// + protected override void OnColumnWidthChanged(DataGridViewColumnEventArgs e) + { + base.OnColumnWidthChanged(e); + + SyncDetachedRoundingScrollbarsFromGridIfIdle(false); + } + #region Column ButtonSpec wiring /// /// Wire column-specific ButtonSpec click events when a column is added. From cd6fc6c409bf8f9e6cda233a5c7351167313eb67 Mon Sep 17 00:00:00 2001 From: tobitege <10787084+tobitege@users.noreply.github.com> Date: Tue, 9 Jun 2026 09:25:15 +0200 Subject: [PATCH 3/4] Fix DataGridView scrollbar resize sync (#3682) --- AGENTS.md | 1 + .../Controls Toolkit/KryptonDataGridView.cs | 32 +++++++++++++++++++ 2 files changed, 33 insertions(+) diff --git a/AGENTS.md b/AGENTS.md index 7249da43c5..86a5c114ec 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -40,6 +40,7 @@ - Direct VS2026 presets: `.\Scripts\Current\build-stable.cmd`, `.\Scripts\Current\build-canary.cmd`, `.\Scripts\Current\build-nightly.cmd`. - Outputs land under `Bin\\\` by default; with `UseArtifactsOutput=true`, outputs land under `artifacts\bin\\\`. - Target frameworks are selected by MSBuild properties. VS2019/full MSBuild builds only .NET Framework 4.x TFMs; VS2022/full MSBuild excludes `net10.0-windows` and `net11.0-windows`; VS2026/full MSBuild excludes `net11.0-windows` unless explicitly enabled; CI or SDK-based builds can include `net472`, `net48`, `net481`, `net8.0-windows`, `net9.0-windows`, `net10.0-windows`, and `net11.0-windows` when the required SDKs are installed. +- New files must use only the current Standard Toolkit BSD header. Do not add the original ComponentFactory BSD header unless the file is derived from original ComponentFactory source. ## Coding Style & Naming Conventions diff --git a/Source/Krypton Components/Krypton.Toolkit/Controls Toolkit/KryptonDataGridView.cs b/Source/Krypton Components/Krypton.Toolkit/Controls Toolkit/KryptonDataGridView.cs index 22f4e01e25..46557d51ca 100644 --- a/Source/Krypton Components/Krypton.Toolkit/Controls Toolkit/KryptonDataGridView.cs +++ b/Source/Krypton Components/Krypton.Toolkit/Controls Toolkit/KryptonDataGridView.cs @@ -183,6 +183,7 @@ private class ToolTipContent : IContentValues private KryptonVScrollBar? _roundingVScrollBar; private KryptonHScrollBar? _roundingHScrollBar; private bool _suppressRoundingScrollSync; + private bool _roundingResizeScrollSyncPending; private int _roundingScrollBarInteractionDepth; private System.Windows.Forms.Timer? _roundingScrollSyncTimer; private string _toolTipText; @@ -1873,6 +1874,7 @@ protected override void OnResize(EventArgs e) { base.OnResize(e); UpdateRoundingAppearance(); + QueueDetachedRoundingScrollbarsResizeSync(); } /// @@ -2796,6 +2798,7 @@ private void DisableDetachedRoundingScrollbars() bool wasUsingDetachedScrollbars = _roundingUsesDetachedScrollbars; _roundingScrollBarInteractionDepth = 0; + _roundingResizeScrollSyncPending = false; if (wasUsingDetachedScrollbars) { ShowNativeScrollbarsAfterRounding(); @@ -3008,6 +3011,35 @@ private void SyncDetachedRoundingScrollbarsFromGridIfIdle(bool layoutScrollbars) } } + private void QueueDetachedRoundingScrollbarsResizeSync() + { + if (!_roundingUsesDetachedScrollbars + || _roundingResizeScrollSyncPending + || !IsHandleCreated + || IsDisposed + || Disposing) + { + return; + } + + _roundingResizeScrollSyncPending = true; + BeginInvoke((System.Windows.Forms.MethodInvoker)(() => + { + _roundingResizeScrollSyncPending = false; + + if (!_roundingUsesDetachedScrollbars + || !IsHandleCreated + || IsDisposed + || Disposing) + { + return; + } + + HideNativeScrollbarsForRounding(); + SyncDetachedRoundingScrollbarsFromGridIfIdle(true); + })); + } + private void UpdateDetachedHorizontalScrollBar() { if (_roundingHScrollBar == null || !WantsDetachedHorizontalScrollBar()) From 1a26b05b908361e00cc3364c3d146ee88c10b59f Mon Sep 17 00:00:00 2001 From: tobitege <10787084+tobitege@users.noreply.github.com> Date: Tue, 9 Jun 2026 17:41:50 +0200 Subject: [PATCH 4/4] 3736-v110-High DPI scaling followup fixes (#3737) * Fix touchscreen DPI scaling artifacts (#2844) * Add changelog for proper Issue --- Documents/Changelog/Changelog.md | 1 + .../Rendering/RenderStandard.cs | 6 +- .../TestForm/TouchscreenHighDpiDemo.cs | 69 +++++++++++++++++++ 3 files changed, 74 insertions(+), 2 deletions(-) diff --git a/Documents/Changelog/Changelog.md b/Documents/Changelog/Changelog.md index cbbc98817f..6c5c326262 100644 --- a/Documents/Changelog/Changelog.md +++ b/Documents/Changelog/Changelog.md @@ -45,6 +45,7 @@ ## 2026-11-xx - Build 2611 (V110 Nightly) - November 2026 +* Resolved [#3736](https://github.com/Krypton-Suite/Standard-Toolkit/issues/3736), Fixed high-dpi scaling causing magenta image borders * Implemented [#3718](https://github.com/Krypton-Suite/Standard-Toolkit/issues/3718), Use 'switch' expression * Resolved [#3720](https://github.com/Krypton-Suite/Standard-Toolkit/issues/3720), `KryptonTreeView` freezes blank after programmatic `SelectedNode = null` then re-select (drain leaked selection `BeginUpdate`/`EndUpdate` batches from #3282/#3498) * Resolved [#3719](https://github.com/Krypton-Suite/Standard-Toolkit/issues/3719), Information exposure through transmitted data diff --git a/Source/Krypton Components/Krypton.Toolkit/Rendering/RenderStandard.cs b/Source/Krypton Components/Krypton.Toolkit/Rendering/RenderStandard.cs index 820037ea00..7d5cb73ba3 100644 --- a/Source/Krypton Components/Krypton.Toolkit/Rendering/RenderStandard.cs +++ b/Source/Krypton Components/Krypton.Toolkit/Rendering/RenderStandard.cs @@ -5732,9 +5732,11 @@ private static void AllocateImageSpace([DisallowNull] StandardContentMemento mem float ratio = Math.Min(displayRect.Width / (float)memento.Image.Width, displayRect.Height / (float)memento.Image.Height); + bool avoidPurple = memento.ImageTransparentColor != GlobalStaticVariables.EMPTY_COLOR; + // Resize image to fit display area memento.Image = CommonHelper.ScaleImageForSizedDisplay(memento.Image, memento.Image.Width * ratio, - memento.Image.Height * ratio, false); + memento.Image.Height * ratio, avoidPurple); } if (memento.Image != null) @@ -12395,4 +12397,4 @@ internal void CalculateOverlayImagePosition() } } #endregion -} \ No newline at end of file +} diff --git a/Source/Krypton Components/TestForm/TouchscreenHighDpiDemo.cs b/Source/Krypton Components/TestForm/TouchscreenHighDpiDemo.cs index d2a47cbcd1..77899cf4ff 100644 --- a/Source/Krypton Components/TestForm/TouchscreenHighDpiDemo.cs +++ b/Source/Krypton Components/TestForm/TouchscreenHighDpiDemo.cs @@ -25,6 +25,7 @@ public partial class TouchscreenHighDpiDemo : KryptonForm { private Timer _dpiMonitorTimer; private bool _updatingFromEvent; + private readonly Dictionary _baselineBounds = new(); public TouchscreenHighDpiDemo() { @@ -48,6 +49,8 @@ private void InitializeForm() // Setup demo controls SetupDemoControls(); + grpSettings.Panel.AutoScroll = true; + // Setup event handlers chkEnableTouchscreen.CheckedChanged += ChkEnableTouchscreen_CheckedChanged; trackScaleFactor.ValueChanged += TrackScaleFactor_ValueChanged; @@ -376,6 +379,8 @@ private void UpdateUIFromSettings() trackFontScaleFactor.Enabled = fontScalingEnabled; lblFontScaleFactor.Enabled = touchscreenEnabled; lblFontScaleValue.Enabled = touchscreenEnabled; + + ApplyScaledDemoLayout(settings); } finally { @@ -383,6 +388,70 @@ private void UpdateUIFromSettings() } } + private void ApplyScaledDemoLayout(TouchscreenSettingValues settings) + { + CaptureBaselineBounds(grpControls.Panel); + CaptureBaselineBounds(grpSettings.Panel); + + float scaleFactor = 1f; + if (settings.TouchscreenModeEnabled) + { + scaleFactor = settings.ControlScaleFactor; + if (settings.FontScalingEnabled) + { + scaleFactor = Math.Max(scaleFactor, settings.FontScaleFactor); + } + } + + scaleFactor = Math.Max(1f, scaleFactor); + + grpControls.Panel.SuspendLayout(); + grpSettings.Panel.SuspendLayout(); + + try + { + foreach (KeyValuePair entry in _baselineBounds) + { + Control control = entry.Key; + if (control.IsDisposed || control.Dock == DockStyle.Fill) + { + continue; + } + + Rectangle bounds = entry.Value; + control.Bounds = new Rectangle( + ScaleLayoutValue(bounds.X, scaleFactor), + ScaleLayoutValue(bounds.Y, scaleFactor), + ScaleLayoutSize(bounds.Width, scaleFactor), + ScaleLayoutSize(bounds.Height, scaleFactor)); + } + } + finally + { + grpSettings.Panel.ResumeLayout(true); + grpControls.Panel.ResumeLayout(true); + } + } + + private void CaptureBaselineBounds(Control parent) + { + foreach (Control child in parent.Controls) + { + if (child.Dock != DockStyle.Fill && !_baselineBounds.ContainsKey(child)) + { + _baselineBounds.Add(child, child.Bounds); + } + + CaptureBaselineBounds(child); + } + } + + private static int ScaleLayoutValue(int value, float scaleFactor) => + Math.Max(0, (int)Math.Round(value * scaleFactor)); + + private static int ScaleLayoutSize(int value, float scaleFactor) => + Math.Max(1, (int)Math.Round(value * scaleFactor)); + private void UpdateStatus() { var settings = KryptonManager.TouchscreenSettingValues;