Skip to content

Commit 4549dd5

Browse files
authored
Merge branch 'alpha' into 2898-feature-request-kryptonhscrollbar-kryptonvscrollbar---part-of-2658
2 parents 34d7859 + 4c70f70 commit 4549dd5

File tree

14 files changed

+796
-79
lines changed

14 files changed

+796
-79
lines changed

Documents/Changelog/Changelog.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
## 2026-11-xx - Build 2611 (V110 Nightly) - November 2026
66

77
* Implemented [#2898](https://github.com/Krypton-Suite/Standard-Toolkit/issues/2898), `KryptonHScrollBar` & `KryptonVScrollBar` - Part of #2658
8+
* Resolved [#2910](https://github.com/Krypton-Suite/Standard-Toolkit/issues/2910), `KryptonComboBox` override Font property causes form designer error
89
* Implemented [#2895](https://github.com/Krypton-Suite/Standard-Toolkit/issues/2895), `KryptonProgressBar` - Three Colour States
910
* Resolved/Implemented [#2844](https://github.com/Krypton-Suite/Standard-Toolkit/issues/2844), Touchscreen High DPI scaling
1011
* Implemented [#2808](https://github.com/Krypton-Suite/Standard-Toolkit/issues/2808), Move `KryptonToastNotification` feature to `Krypton.Utilities`

Source/Krypton Components/Krypton.Toolkit/ButtonSpec/ButtonSpecManagerBase.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -857,7 +857,7 @@ private ViewDockStyle GetDockStyle(ButtonSpec spec)
857857
{
858858
var edge = spec.GetEdge(_redirector);
859859

860-
var isRtl = Control is Form form && form.RightToLeft == RightToLeft.Yes && form.RightToLeftLayout;
860+
var isRtl = CommonHelper.IsRightToLeftLayout(Control);
861861

862862
// In RTL mode with RightToLeftLayout enabled, reverse the dock style
863863
return isRtl ? edge == RelativeEdgeAlign.Near ? ViewDockStyle.Right : ViewDockStyle.Left :

Source/Krypton Components/Krypton.Toolkit/Controls Toolkit/KryptonComboBox.cs

Lines changed: 55 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1503,9 +1503,15 @@ public override Color BackColor
15031503
[AllowNull]
15041504
public override Font Font
15051505
{
1506-
get => base.Font;
1506+
get => GetStateCommonFont() ?? base.Font;
15071507

1508-
set => base.Font = value!;
1508+
set
1509+
{
1510+
// Always set base.Font to ensure consistency
1511+
base.Font = value!;
1512+
// Also try to set StateCommon font, but don't fail if it's not available
1513+
SetStateCommonFont(value);
1514+
}
15091515
}
15101516

15111517
/// <summary>
@@ -2477,6 +2483,53 @@ protected virtual void OnHoverSelectionChanged(HoveredSelectionChangedEventArgs
24772483
/// </summary>
24782484
/// <param name="e"></param>
24792485
protected virtual void OnToolTipNeeded(ToolTipNeededEventArgs e) => ToolTipNeeded?.Invoke(this, e);
2486+
2487+
/// <summary>
2488+
/// Gets the font from StateCommon.ComboBox.Content.Font safely, handling design mode serialization issues.
2489+
/// </summary>
2490+
/// <returns>The font from StateCommon, or null if not available or during problematic design time access.</returns>
2491+
protected virtual Font? GetStateCommonFont()
2492+
{
2493+
try
2494+
{
2495+
// Use null-conditional operators to safely access nested properties
2496+
// This prevents issues during design time serialization when StateCommon might not be fully initialized
2497+
return StateCommon.ComboBox.Content?.Font;
2498+
}
2499+
catch
2500+
{
2501+
// If StateCommon is not fully initialized or there's a serialization issue,
2502+
// return null to fall back to base.Font
2503+
return null;
2504+
}
2505+
}
2506+
2507+
/// <summary>
2508+
/// Sets the font to StateCommon.ComboBox.Content.Font safely, handling design mode serialization issues.
2509+
/// </summary>
2510+
/// <param name="value">The font value to set.</param>
2511+
/// <returns>True if the font was set to StateCommon, false to fall back to base.Font.</returns>
2512+
protected virtual bool SetStateCommonFont(Font? value)
2513+
{
2514+
try
2515+
{
2516+
// Use null-conditional operators to safely access nested properties
2517+
// This prevents issues during design time serialization when StateCommon might not be fully initialized
2518+
if (StateCommon.ComboBox?.Content != null)
2519+
{
2520+
StateCommon.ComboBox.Content.Font = value;
2521+
2522+
return true;
2523+
}
2524+
}
2525+
catch
2526+
{
2527+
// If StateCommon is not fully initialized or there's a serialization issue,
2528+
// return false to fall back to base.Font
2529+
}
2530+
2531+
return false;
2532+
}
24802533
// ReSharper restore VirtualMemberNeverOverridden.Global
24812534
#endregion
24822535

Source/Krypton Components/Krypton.Toolkit/Controls Toolkit/KryptonMonthCalendar.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -290,6 +290,7 @@ public override AutoSizeMode AutoSizeMode
290290
set => base.AutoSizeMode = value;
291291
}
292292

293+
293294
/// <summary>
294295
/// Gets or sets the Input Method Editor (IME) mode of the control.
295296
/// </summary>
@@ -1479,6 +1480,7 @@ protected override void OnLostFocus(EventArgs e)
14791480
base.OnLostFocus(e);
14801481
}
14811482

1483+
14821484
/// <summary>
14831485
/// Raises the Paint event.
14841486
/// </summary>

Source/Krypton Components/Krypton.Toolkit/Controls Visuals/VisualSimpleBase.cs

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,19 @@ namespace Krypton.Toolkit;
1919
[DesignerCategory(@"code")]
2020
public abstract class VisualSimpleBase : VisualControlBase
2121
{
22+
#region Instance Fields
23+
24+
private bool _isRightToLeftLayout;
25+
26+
#endregion
27+
2228
#region Identity
2329
/// <summary>
2430
/// Initialize a new instance of the VisualSimpleBase class.
2531
/// </summary>
2632
protected VisualSimpleBase()
2733
{
34+
_isRightToLeftLayout = false;
2835
}
2936
#endregion
3037

@@ -172,5 +179,76 @@ public override Color ForeColor
172179
get => base.ForeColor;
173180
set => base.ForeColor = value;
174181
}
182+
183+
/// <summary>
184+
/// Gets or sets a value indicating whether control's elements are aligned to support locales using right-to-left fonts.
185+
/// </summary>
186+
[Category(@"Appearance")]
187+
[Localizable(true)]
188+
[Description(@"Indicates whether the control should support RightToLeft layouts.")]
189+
[DefaultValue(typeof(RightToLeft), "No")]
190+
public override RightToLeft RightToLeft
191+
{
192+
get => base.RightToLeft;
193+
set
194+
{
195+
if (base.RightToLeft != value)
196+
{
197+
base.RightToLeft = value;
198+
PerformNeedPaint(true);
199+
}
200+
}
201+
}
202+
203+
/// <summary>
204+
/// Gets or sets a value indicating whether the layout of the control is from right to left.
205+
/// </summary>
206+
[Category(@"Appearance")]
207+
[Localizable(true)]
208+
[Description(@"Indicates whether the layout of the control is from right to left.")]
209+
[DefaultValue(false)]
210+
[Browsable(true)]
211+
[EditorBrowsable(EditorBrowsableState.Always)]
212+
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
213+
public bool RightToLeftLayout
214+
{
215+
get => _isRightToLeftLayout;
216+
set
217+
{
218+
if (_isRightToLeftLayout != value)
219+
{
220+
_isRightToLeftLayout = value;
221+
OnRightToLeftLayoutChanged(EventArgs.Empty);
222+
PerformNeedPaint(true);
223+
}
224+
}
225+
}
226+
#endregion
227+
228+
#region Protected Overrides
229+
/// <summary>
230+
/// Raises the RightToLeftChanged event.
231+
/// </summary>
232+
/// <param name="e">An EventArgs containing event data.</param>
233+
protected override void OnRightToLeftChanged(EventArgs e)
234+
{
235+
// Need re-layout to reflect change of layout direction
236+
PerformNeedPaint(true);
237+
238+
base.OnRightToLeftChanged(e);
239+
}
240+
241+
/// <summary>
242+
/// Raises the RightToLeftLayoutChanged event.
243+
/// </summary>
244+
/// <param name="e">An EventArgs containing event data.</param>
245+
/// <remarks>
246+
/// This method is provided for controls that don't have OnRightToLeftLayoutChanged in their base class.
247+
/// Derived classes can override this to provide custom handling when RightToLeftLayout changes.
248+
/// </remarks>
249+
protected virtual void OnRightToLeftLayoutChanged(EventArgs e) =>
250+
// Need re-layout to reflect change of layout direction
251+
PerformNeedPaint(true);
252+
175253
#endregion
176254
}

Source/Krypton Components/Krypton.Toolkit/General/CommonHelper.cs

Lines changed: 54 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -421,29 +421,70 @@ public static Padding OrientatePadding(VisualOrientation orientation,
421421
public static void SwapRectangleSizes(ref Rectangle rect) => (rect.Width, rect.Height) = (rect.Height, rect.Width);
422422

423423
/// <summary>
424-
/// Gets the form level right to left setting.
424+
/// Gets the right to left layout setting for a control.
425425
/// </summary>
426426
/// <param name="control">Control for which the setting is needed.</param>
427-
/// <returns>RightToLeft setting.</returns>
427+
/// <returns>RightToLeftLayout setting.</returns>
428428
public static bool GetRightToLeftLayout(Control control)
429429
{
430-
// Default to left-to-right layout
431-
var rtl = false;
432-
433-
// We need a valid control to find a top level form
434-
// Search for a top level form associated with the control
435-
Form? topForm = control.FindForm();
430+
// First check if the control itself has RightToLeftLayout (e.g., VisualSimpleBase controls)
431+
if (control is VisualSimpleBase visualSimpleBase)
432+
{
433+
return visualSimpleBase.RightToLeftLayout;
434+
}
436435

437-
// If can find an owning form
438-
if (topForm != null)
436+
// For other controls that might have RightToLeftLayout (like Form, ListView, etc.)
437+
// Use reflection to check if the property exists and get its value
438+
var property = control.GetType().GetProperty("RightToLeftLayout");
439+
if (property != null && property.PropertyType == typeof(bool))
439440
{
440-
// Use the form setting instead
441-
rtl = topForm.RightToLeftLayout;
441+
if (property.GetValue(control) is bool value)
442+
{
443+
return value;
444+
}
442445
}
443446

444-
return rtl;
447+
// Default to left-to-right layout
448+
return false;
445449
}
446450

451+
/// <summary>
452+
/// Determines if RTL layout should be applied for the given control.
453+
/// </summary>
454+
/// <param name="control">Control for which RTL is checked.</param>
455+
/// <returns>True if RTL layout should be applied; otherwise false.</returns>
456+
public static bool IsRightToLeftLayout(Control control) => GetRightToLeftLayout(control) && (control.RightToLeft == RightToLeft.Yes);
457+
458+
/// <summary>
459+
/// Calculates the X position from the right edge for RTL-aware positioning.
460+
/// </summary>
461+
/// <param name="containerWidth">Width of the container.</param>
462+
/// <param name="itemWidth">Width of the item to position.</param>
463+
/// <param name="offset">Offset from the edge.</param>
464+
/// <param name="isRtl">True if RTL layout is active.</param>
465+
/// <returns>X position for the item.</returns>
466+
public static int GetRtlAwareXPosition(int containerWidth, int itemWidth, int offset, bool isRtl) =>
467+
isRtl
468+
? containerWidth - itemWidth - offset // Position from right
469+
: offset; // Position from left
470+
471+
/// <summary>
472+
/// Calculates the X position increment for RTL-aware layout (positive or negative step).
473+
/// </summary>
474+
/// <param name="step">Step size to increment by.</param>
475+
/// <param name="isRtl">True if RTL layout is active.</param>
476+
/// <returns>Step value (negative for RTL, positive for LTR).</returns>
477+
public static int GetRtlAwareStep(int step, bool isRtl) => isRtl ? -step : step;
478+
479+
/// <summary>
480+
/// Gets the reverse index for accessing items in reverse order for RTL.
481+
/// </summary>
482+
/// <param name="index">Original index (0-based).</param>
483+
/// <param name="totalCount">Total number of items.</param>
484+
/// <param name="isRtl">True if RTL layout is active.</param>
485+
/// <returns>Index to use for accessing the item (reversed if RTL).</returns>
486+
public static int GetRtlAwareIndex(int index, int totalCount, bool isRtl) => isRtl ? (totalCount - 1 - index) : index;
487+
447488
/// <summary>
448489
/// Decide if the context menu strip should be Displayed.
449490
/// </summary>

Source/Krypton Components/Krypton.Toolkit/View Draw/ViewDrawMonth.cs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@ public ViewDrawMonth(IKryptonMonthCalendar calendar,
8585
Add(_drawHeader);
8686

8787
// Create the left/right arrows for moving the months
88+
// Note: ButtonSpecManagerBase.GetDockStyle() handles RTL swapping automatically
8889
_arrowPrev = new ButtonSpecCalendar(this, PaletteButtonSpecStyle.Previous, RelativeEdgeAlign.Near);
8990
_arrowNext = new ButtonSpecCalendar(this, PaletteButtonSpecStyle.Next, RelativeEdgeAlign.Far);
9091
_arrowPrev.Click += OnPrevMonth;
@@ -124,14 +125,16 @@ public ViewDrawMonth(IKryptonMonthCalendar calendar,
124125
_borderEdge = new PaletteBorderEdge(_borderEdgeRedirect, null);
125126
_drawBorderEdge = new ViewDrawBorderEdge(_borderEdge, Orientation.Vertical);
126127
_drawWeekNumbers = new ViewDrawWeekNumbers(_calendar, _months);
127-
var borderLeftDock = new ViewLayoutDocker
128+
129+
// Create a docker for week numbers - use ViewLayoutDocker which automatically handles RTL via CalculateDock
130+
var borderWeekDock = new ViewLayoutDocker
128131
{
129132
{ _drawWeekNumbers, ViewDockStyle.Left },
130133
{ new ViewLayoutSeparator(0, 4), ViewDockStyle.Top },
131134
{ _drawBorderEdge, ViewDockStyle.Fill },
132135
{ new ViewLayoutSeparator(0, 4), ViewDockStyle.Bottom }
133136
};
134-
_numberStack.Add(borderLeftDock);
137+
_numberStack.Add(borderWeekDock);
135138

136139
// Add border between day names and individual days
137140
var borderEdgeRedirect = new PaletteBorderEdgeRedirect(_calendar.StateNormal.Header.Border, null);

0 commit comments

Comments
 (0)