diff --git a/Documents/Changelog/Changelog.md b/Documents/Changelog/Changelog.md index 9353654c57..fff704d5c5 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 [#3183](https://github.com/Krypton-Suite/Standard-Toolkit/issues/3183), Small square rendered next to Close button on KryptonForm when using a custom theme * Resolved [#3283](https://github.com/Krypton-Suite/Standard-Toolkit/issues/3283), `KryptonThemeComboBox` does not change theme when `SelectedIndex` is changed * Resolved [#3330](https://github.com/Krypton-Suite/Standard-Toolkit/issues/3330), `KryptonManager` exception 'The type initializer for 'Krypton.Toolkit.KryptonManager' threw an exception.' * Implemented [#1673](https://github.com/Krypton-Suite/Standard-Toolkit/issues/1673), `KryptonContextMenuComboBox` & `KryptonContextMenuProgressBar` need to be implemented. `KryptonContextMenuProgressBar` for context menus (and enabled **Add ComboBox** in the context menu collection editor). diff --git a/Source/Krypton Components/Krypton.Toolkit/Controls Toolkit/KryptonCustomPaletteBase.cs b/Source/Krypton Components/Krypton.Toolkit/Controls Toolkit/KryptonCustomPaletteBase.cs index ec7d0c3d3b..040caba6ee 100644 --- a/Source/Krypton Components/Krypton.Toolkit/Controls Toolkit/KryptonCustomPaletteBase.cs +++ b/Source/Krypton Components/Krypton.Toolkit/Controls Toolkit/KryptonCustomPaletteBase.cs @@ -3124,6 +3124,20 @@ private void ImportFromXmlDocument([DisallowNull] XmlDocument doc) SetPaletteName(root.GetAttribute("Name")); } + // Import order: establish a full baseline from BasePalette, then apply the XML theme on top. + // + // PopulateFromBaseOperation resets the palette hierarchy and copies values from the current + // BasePalette (via each storage type's PopulateFromBase). That ensures every property that the + // theme file does NOT serialize still reflects inheritance from the base theme instead of + // leftover C# defaults or stale state from a previous load—avoiding gaps in metrics, button + // specs, header chrome, etc. (e.g. stray rendering next to caption buttons). + // + // The steps below (ImportImagesFromElement / ImportObjectFromElement) then overwrite only what + // the XML actually defines. Theme wins for anything present in the file; anything omitted keeps + // the values we just populated from BasePalette. Doing populate after import would fight the + // file and could reset explicit theme settings, so populate must run first. + PopulateFromBaseOperation(null); + // Grab the properties and images elements var props = root.SelectSingleNode(nameof(Properties)) as XmlElement; var images = root.SelectSingleNode(nameof(Images)) as XmlElement; @@ -3538,11 +3552,29 @@ private void ExportObjectToElement(XmlDocument doc, // Should we test if the property value is the default? if (ignoreDefaults) { + // First prefer the component-model pattern used across the palette hierarchy. + // If a ShouldSerialize() method exists and returns false, treat + // the property as default and do not export it. + var shouldSerializeMethod = t.GetMethod($"ShouldSerialize{prop.Name}", + BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, + binder: null, + types: Type.EmptyTypes, + modifiers: null); + + if (shouldSerializeMethod != null && + shouldSerializeMethod.ReturnType == typeof(bool)) + { + if (!(bool)shouldSerializeMethod.Invoke(obj, null)!) + { + ignore = true; + } + } + var defaultAttribs = prop.GetCustomAttributes(typeof(DefaultValueAttribute), false); // Does this property have a default value attribute? // Use the first one found (KryptonDefaultColor is a DefaultValueAttribute subclass) - if (defaultAttribs.Length >= 1) + if (!ignore && defaultAttribs.Length >= 1) { // Cast to correct type var defaultAttrib = defaultAttribs[0] as DefaultValueAttribute; diff --git a/Source/Krypton Components/TestForm/Bug3183SmallSquareRenderedNextToClose.Designer.cs b/Source/Krypton Components/TestForm/Bug3183SmallSquareRenderedNextToClose.Designer.cs new file mode 100644 index 0000000000..20d53e04ce --- /dev/null +++ b/Source/Krypton Components/TestForm/Bug3183SmallSquareRenderedNextToClose.Designer.cs @@ -0,0 +1,75 @@ +namespace TestForm +{ + partial class Bug3183SmallSquareRenderedNextToClose + { + /// + /// 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(); + } + 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.components = new System.ComponentModel.Container(); + this.kryptonButton1 = new Krypton.Toolkit.KryptonButton(); + this.kryptonManager1 = new Krypton.Toolkit.KryptonManager(this.components); + this.SuspendLayout(); + // + // kryptonButton1 + // + this.kryptonButton1.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); + this.kryptonButton1.Location = new System.Drawing.Point(465, 45); + this.kryptonButton1.Name = "kryptonButton1"; + this.kryptonButton1.Size = new System.Drawing.Size(290, 90); + this.kryptonButton1.TabIndex = 2; + this.kryptonButton1.Values.DropDownArrowColor = System.Drawing.Color.Empty; + this.kryptonButton1.Values.Text = "Apply Pink Theme"; + this.kryptonButton1.Click += new System.EventHandler(this.kryptonButton1_Click); + // + // kryptonManager1 + // + this.kryptonManager1.ToolkitStrings.MessageBoxStrings.LessDetails = "L&ess Details..."; + this.kryptonManager1.ToolkitStrings.MessageBoxStrings.MoreDetails = "&More Details..."; + this.kryptonManager1.ToolkitStrings.PrintPreviewDialogStrings.PaginationPartOneText = "of"; + this.kryptonManager1.ToolkitStrings.PrintPreviewDialogStrings.PaginationPartTwoText = null; + this.kryptonManager1.ToolkitStrings.PrintPreviewDialogStrings.ZoomInButtonText = "Zoom &Out"; + this.kryptonManager1.ToolkitStrings.PrintPreviewDialogStrings.ZoomOutButtonText = null; + // + // Bug3183SmallSquareRenderedNextToClose + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.ClientSize = new System.Drawing.Size(800, 450); + this.Controls.Add(this.kryptonButton1); + this.Name = "Bug3183SmallSquareRenderedNextToClose"; + this.Text = "Bug3183SmallSquareRenderedNextToClose"; + this.WindowState = System.Windows.Forms.FormWindowState.Maximized; + this.Load += new System.EventHandler(this.Bug3183SmallSquareRenderedNextToClose_Load); + this.ResumeLayout(false); + + } + + #endregion + + private KryptonButton kryptonButton1; + private KryptonManager kryptonManager1; + } +} \ No newline at end of file diff --git a/Source/Krypton Components/TestForm/Bug3183SmallSquareRenderedNextToClose.cs b/Source/Krypton Components/TestForm/Bug3183SmallSquareRenderedNextToClose.cs new file mode 100644 index 0000000000..8b3d1909c2 --- /dev/null +++ b/Source/Krypton Components/TestForm/Bug3183SmallSquareRenderedNextToClose.cs @@ -0,0 +1,70 @@ +#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; +using System.Collections.Generic; +using System.ComponentModel; +using System.Data; +using System.Drawing; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows.Forms; + +using TestForm.Properties; + +namespace TestForm +{ + /// + /// Manual repro harness for GitHub issue #3183: a small square or wrong caption glyph next to the + /// Close (and related) buttons when a custom XML palette is applied. + /// + /// + /// + /// The embedded resource Microsoft365_Super_Pink matches the theme attached to the issue. + /// Run the form, then click Apply Pink Theme and inspect the top-right caption buttons + /// (especially when maximized vs normal). + /// + /// + /// The form starts maximized to mirror the original reporter’s sample and to exercise maximize / + /// restore button-spec state. + /// + /// + public partial class Bug3183SmallSquareRenderedNextToClose : KryptonForm + { + public Bug3183SmallSquareRenderedNextToClose() + { + InitializeComponent(); + } + + /// + /// No startup theme load: compare default chrome first, then apply the custom palette from the button. + /// + private void Bug3183SmallSquareRenderedNextToClose_Load(object sender, EventArgs e) + { + } + + /// + /// Loads the embedded pink theme and switches the global palette to Custom so this form’s caption + /// uses the same path as real apps (). + /// + private void kryptonButton1_Click(object sender, EventArgs e) + { + // Theme XML is stored as a string resource so the repro has no dependency on disk paths. + byte[] contentFile = Encoding.UTF8.GetBytes(Properties.Resources.Microsoft365_Super_Pink); + + KryptonCustomPaletteBase customPaletteBase = new KryptonCustomPaletteBase(); + customPaletteBase.ImportWithUpgrade(new MemoryStream(contentFile)); + + this.kryptonManager1.GlobalCustomPalette = customPaletteBase; + this.kryptonManager1.GlobalPaletteMode = PaletteMode.Custom; + } + } +} diff --git a/Source/Krypton Components/TestForm/Bug3183SmallSquareRenderedNextToClose.resx b/Source/Krypton Components/TestForm/Bug3183SmallSquareRenderedNextToClose.resx new file mode 100644 index 0000000000..d0bfb46ccb --- /dev/null +++ b/Source/Krypton Components/TestForm/Bug3183SmallSquareRenderedNextToClose.resx @@ -0,0 +1,123 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 17, 17 + + \ No newline at end of file diff --git a/Source/Krypton Components/TestForm/Properties/Resources.Designer.cs b/Source/Krypton Components/TestForm/Properties/Resources.Designer.cs index df61b3c794..ab6056d33d 100644 --- a/Source/Krypton Components/TestForm/Properties/Resources.Designer.cs +++ b/Source/Krypton Components/TestForm/Properties/Resources.Designer.cs @@ -1760,6 +1760,27 @@ internal static System.Drawing.Bitmap mh { } } + /// + /// Looks up a localized string similar to <?xml version='1.0' encoding='utf-8'?> + ///<KryptonPalette Version="21" Generated="Monday, 09 March 2026, @15:54"> + /// <Properties> + /// <UseThemeFormChromeBorderWidth Type="InheritBool" Value="Inherit" /> + /// <ButtonSpecs> + /// <Common> + /// <Image Type="Image" Value="" /> + /// <ImageStates> + /// <ImageCheckedNormal Type="Image" Value="" /> + /// <ImageCheckedPressed Type="Image" Value="" /> + /// <ImageCheckedTracking Type="Image" Value="" /> + /// <ImageNormal Type="Image" Value="" /> + /// [rest of string was truncated]";. + /// + internal static string Microsoft365_Super_Pink { + get { + return ResourceManager.GetString("Microsoft365_Super_Pink", resourceCulture); + } + } + /// /// Looks up a localized resource of type System.Drawing.Bitmap. /// diff --git a/Source/Krypton Components/TestForm/Properties/Resources.resx b/Source/Krypton Components/TestForm/Properties/Resources.resx index 284067406a..2056775a58 100644 --- a/Source/Krypton Components/TestForm/Properties/Resources.resx +++ b/Source/Krypton Components/TestForm/Properties/Resources.resx @@ -946,4 +946,7 @@ ..\Resources\delete.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + ..\Resources\Microsoft365 Super Pink.xml;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;Windows-1252 + \ No newline at end of file diff --git a/Source/Krypton Components/TestForm/StartScreen.cs b/Source/Krypton Components/TestForm/StartScreen.cs index aa5e6abe65..5e99d16872 100644 --- a/Source/Krypton Components/TestForm/StartScreen.cs +++ b/Source/Krypton Components/TestForm/StartScreen.cs @@ -150,6 +150,7 @@ private void AddButtons() CreateButton("Tabbed MDI Demo (Issue #1746)", "KryptonTabbedMdiManager: MDI child windows displayed as tab pages instead of overlapping windows."); CreateButton("Ribbon MDI Demo (Issue #2921)", "Comprehensive demo for Issue #2921: Ribbon + MDI. Verifies no double ribbon tabs when opening/closing maximized MDI children; close/minimize/maximize and QAT click areas aligned with visuals."); CreateButton("Ribbon QATLocation=Hidden does not hide QAT when FormBorderStyle=None (Issue #3203)", string.Empty); + CreateButton("Small Square Rendered Next to Close Button (Issue #3183)", string.Empty); } private void OnFormClosing(object? sender, FormClosingEventArgs e)