diff --git a/.github/skills/syncfusion-blazor-toolkit-charts/SKILL.md b/.github/skills/syncfusion-blazor-toolkit-charts/SKILL.md index d850dfc..e91460e 100644 --- a/.github/skills/syncfusion-blazor-toolkit-charts/SKILL.md +++ b/.github/skills/syncfusion-blazor-toolkit-charts/SKILL.md @@ -581,12 +581,41 @@ Here's a minimal example to create a column chart with data: ### Chart Component (`SfChart`) - `Title` - Chart title text - `Width` / `Height` - Chart dimensions (accepts px or %, default: "100%") -- `Theme` - Visual theme (see Theme enum in API reference) +- `Theme` - Visual theme. One of `Theme.Fluent` (default), `Theme.FluentDark`, `Theme.HighContrast`. See the **Themes** section below. - `Background` - Chart background color - `EnableAnimation` - Enable/disable animation (default: true) - `SelectionMode` - Selection mode (None, Series, Point, Cluster, DragXY, DragX, DragY, Lasso) - `HighlightMode` - Highlight mode (None, Series, Point, Cluster) +### Themes + +The chart's `Theme` parameter is bound to `Syncfusion.Blazor.Toolkit.Theme`: + +| Enum value | Background | Text | Focus / Selection | Use case | +|---|---|---|---|---| +| `Theme.Fluent` (default) | `#FFFFFF` | `#242424` | `#0F6CBD` | Light mode interfaces. | +| `Theme.FluentDark` | `#1C1B1F` | `#FFFFFF` | `#115EA3` | Dark mode interfaces. | +| `Theme.HighContrast` | `#000000` | `#FFFFFF` | `#FFFF00` | Accessibility scenarios; matches Microsoft High Contrast. | + +The theme flows into: +- `ChartHelper.GetChartThemeStyle(...)` — tokens for axis lines, labels, grid lines, titles, legend, tooltips, crosshair, selection rectangles, error bars, stripline text, tooltip fill, and the scrollbar (`ChartHelper.GetScrollbarThemeColor`). +- `ChartHelper.GetSeriesColor(...)` — the default series palette when `Palettes` is not supplied. The high-contrast palette uses fully saturated, easily distinguishable colors: `#FFFF00`, `#00FFFF`, `#FF00FF`, `#00FF00`, `#FF8000`, `#80FF00`, `#00FF80`, `#FF0080`, `#FFFFFF`, `#80FFFF`. +- `ZoomToolkit` — the zoom toolbar icon colors and rectangle fill re-skin to match the active theme. + +**Example — High Contrast:** + +```razor +@using Syncfusion.Blazor.Toolkit.Charts + + + + + +``` + +> The high-contrast theme provides maximum contrast and is recommended for users who have Windows High Contrast Mode (or an equivalent OS-level accessibility setting) enabled. When a custom `Background` or `Palettes` is supplied, those values always win over the theme defaults. + ### Primary Axes (`ChartPrimaryXAxis`, `ChartPrimaryYAxis`) - `ValueType` - Axis type: `Syncfusion.Blazor.Toolkit.ValueType.Category`, `ValueType.Double`, `ValueType.DateTime`, `ValueType.Logarithmic`, `ValueType.DateTimeCategory` - `Title` - Axis title diff --git a/src/Base/Enumeration.cs b/src/Base/Enumeration.cs index 31e1213..ae64aec 100644 --- a/src/Base/Enumeration.cs +++ b/src/Base/Enumeration.cs @@ -1362,7 +1362,17 @@ public enum Theme /// /// The Fluent dark theme is designed for dark mode interfaces. /// - FluentDark + FluentDark, + /// + /// Applies the High Contrast theme to the chart, rendering with maximum-contrast colors for users who require stronger visual differentiation. + /// + /// + /// The High Contrast theme uses a black background (#000000), white text (#FFFFFF), + /// a soft accessibility-yellow focus / selection color (#FFD939), and a high-contrast accent palette + /// (axis labels and grid lines in neutral grays #969696 / #BFBFBF). + /// It is designed for accessibility scenarios and environments where Windows High Contrast Mode (or an equivalent OS-level accessibility setting) is enabled. + /// + HighContrast } /// diff --git a/src/Components/Charts/Chart/Renderer/UserInteractions/ZoomToolkit.cs b/src/Components/Charts/Chart/Renderer/UserInteractions/ZoomToolkit.cs index cd52351..3d80a2c 100644 --- a/src/Components/Charts/Chart/Renderer/UserInteractions/ZoomToolkit.cs +++ b/src/Components/Charts/Chart/Renderer/UserInteractions/ZoomToolkit.cs @@ -58,11 +58,15 @@ public class ZoomToolkit : ComponentBase protected override void OnInitialized() { _elementId = Chart?.ID; - _selectionColor = Chart?.Theme != Theme.FluentDark ? "#424242" : "#D6D6D6"; - _fillColor = Chart?.Theme != Theme.FluentDark ? "#424242" : "#D6D6D6"; - _iconRectOverFill = Chart?.Theme != Theme.FluentDark ? "#EBEBEB" : "#383838"; - _iconRectSelectionFill = Chart?.Theme != Theme.FluentDark ? "#EBEBEB" : "#383838"; - _iconRect = Chart?.Theme != Theme.FluentDark ? new Rect(-7, -8, 32, 32) : new Rect(0, 0, 16, 16); + // Resolve theme-aware color tokens for the zoom toolkit. High Contrast is treated as a third branch. + // The soft accessibility yellow (#FFD939) matches the selectionCircleStroke / tabColor tokens in ChartHelper.GetThemeStyle("HighContrast"). + bool isHighContrast = Chart?.Theme == Theme.HighContrast; + bool isDark = Chart?.Theme == Theme.FluentDark; + _selectionColor = isHighContrast ? "#FFD939" : (isDark ? "#D6D6D6" : "#424242"); + _fillColor = isHighContrast ? "#FFD939" : (isDark ? "#D6D6D6" : "#424242"); + _iconRectOverFill = isHighContrast ? "#000000" : (isDark ? "#383838" : "#EBEBEB"); + _iconRectSelectionFill = isHighContrast ? "#000000" : (isDark ? "#383838" : "#EBEBEB"); + _iconRect = isHighContrast ? new Rect(0, 0, 16, 16) : (isDark ? new Rect(0, 0, 16, 16) : new Rect(-7, -8, 32, 32)); _zoomkitOpacity = 1; } @@ -124,7 +128,7 @@ private void ShowZoomingToolkit(RenderTreeBuilder builder) } toolboxItems = IsDevice() ? [ToolbarItems.Reset] : toolboxItems; - RectOptions rectOptions = new(_elementId + "_Zooming_Rect", 0, 0, width, height + (SPACING * 2), 1, Constants.Transparent, Chart?.Theme != Theme.FluentDark ? "#fafafa" : "#1C1B1F", 4, 4, 1); + RectOptions rectOptions = new(_elementId + "_Zooming_Rect", 0, 0, width, height + (SPACING * 2), 1, Constants.Transparent, Chart?.Theme == Theme.HighContrast ? "#000000" : (Chart?.Theme != Theme.FluentDark ? "#fafafa" : "#1C1B1F"), 4, 4, 1); RenderZoomKit(builder, transX, transY, rectOptions, length, toolboxItems, iconSize); } @@ -361,7 +365,7 @@ private void RenderResetButtonAsText(SvgRendering render, RenderTreeBuilder buil Transform = "rotate(0," + 0 + ',' + 0 + ')', DominantBaseline = "middle", FontSize = "12px", - Fill = Chart?.Theme != Theme.FluentDark ? "black" : "white" + Fill = Chart?.Theme == Theme.HighContrast ? "#FFD939" : (Chart?.Theme != Theme.FluentDark ? "black" : "white") }); } diff --git a/src/Components/Charts/Common/ChartUtils/ChartHelper.cs b/src/Components/Charts/Common/ChartUtils/ChartHelper.cs index 76d12ef..7b8c375 100644 --- a/src/Components/Charts/Common/ChartUtils/ChartHelper.cs +++ b/src/Components/Charts/Common/ChartUtils/ChartHelper.cs @@ -1252,16 +1252,21 @@ internal static string FindThemeColor(string actualColor, string themeColor) /// The theme style configuration. internal static ChartThemeStyle GetChartThemeStyle(string theme) { - if(theme != "FluentDark") - { - return GetThemeStyle("#616161", "#242424", "#D2D0CE", "#EDEBE9", "#EDEBE9", "#D2D0CE", "#D2DOCE", "#242424", "#242424", "#FFFFFF", "#EDEBE9", "#A19F9D", "#A19F9D", "rgba(138, 136, 134, 0.1)", "#FFFFFF", "#242424", "#FFFFFF", "#242424", "#242424", "#D2D0CE", null!, "rgba(180, 214, 250, 0.1)", "#0F6CBD", "#0F6CBD", "#424242", "#A19F9D", - "14px", "600", "Segoe UI", "12px", "400", "Segoe UI", "12px", "Segoe UI", "400", "12px", "Segoe UI", "700", "12px", "Segoe UI", "400", "12px", "Segoe UI", "600", "#616161", "12px", "Segoe UI", "600", "#616161", "Segoe UI", "12px", "400", "#E7910F", "#0076E5", "12px", "Segoe UI", "600"); - } - else + if (theme == "FluentDark") { return GetThemeStyle("#ADADAD", "#FFFFFF", "#3B3A39", "#292827", "#292827", "#3B3A39", "#3B3A39", "#FFFFFF", "#FFFFFF", "#1c1b1f", "#292827", "#8A8886", "#8A8886", "rgba(138, 136, 134, 0.1)", "#292929", "#FFFFFF", "#292929", "#FFFFFF", "#FFFFFF", "#3B3A39", null!, "rgba(14, 71, 117, 0.1)", "#115EA3", "#115EA3", "#D6D6D6", "#8A8886", "14px", "600", "Segoe UI", "12px", "400", "Segoe UI", "12px", "Segoe UI", "400", "12px", "Segoe UI", "700", "12px", "Segoe UI", "400", "12px", "Segoe UI", "600", "#ADADAD", "12px", "Segoe UI", "600", "#ADADAD", "Segoe UI", "12px", "400", "#584EC6", "#43B786", "12px", "Segoe UI", "600"); } + if (theme == "HighContrast") + { + // High Contrast theme: black background, light-gray axis labels, white chart title. + // Yellow (#FFD939) is used for focus/selection accents to remain visible on black. + return GetThemeStyle("#969696", "#FFFFFF", "#ffffff", "#BFBFBF", "#969696", "#BFBFBF", "#969696", "#FFFFFF", "#969696", "#000000", "#ffffff", "#ffffff", "#ffffff", "rgba(255, 255, 255, 0.1)", "#FFFFFF", "#000000", "#ffffff", "#000000", "#000000", "#969696", "#BFBFBF", "rgba(255, 217, 57, 0.3)", "#ffffff", "#FFD939", "#FFD939", "#ffffff", + "16px", "600", "Segoe UI", "12px", "400", "Segoe UI", "14px", "Segoe UI", "400", "12px", "Segoe UI", "400", "12px", "Segoe UI", "400", "14px", "Segoe UI", "600", "#FFFFFF", "14px", "Segoe UI", "400", "#969696", "Segoe UI"); + } + // Fluent (light) — default. + return GetThemeStyle("#616161", "#242424", "#D2D0CE", "#EDEBE9", "#EDEBE9", "#D2D0CE", "#D2DOCE", "#242424", "#242424", "#FFFFFF", "#EDEBE9", "#A19F9D", "#A19F9D", "rgba(138, 136, 134, 0.1)", "#FFFFFF", "#242424", "#FFFFFF", "#242424", "#242424", "#D2D0CE", null!, "rgba(180, 214, 250, 0.1)", "#0F6CBD", "#0F6CBD", "#424242", "#A19F9D", + "14px", "600", "Segoe UI", "12px", "400", "Segoe UI", "12px", "Segoe UI", "400", "12px", "Segoe UI", "700", "12px", "Segoe UI", "400", "12px", "Segoe UI", "600", "#616161", "12px", "Segoe UI", "600", "#616161", "Segoe UI", "12px", "400", "#E7910F", "#0076E5", "12px", "Segoe UI", "600"); } /// @@ -1270,14 +1275,16 @@ internal static ChartThemeStyle GetChartThemeStyle(string theme) /// An array of color strings for the series. internal static string[] GetSeriesColor(string theme) { - if(theme != "FluentDark") + if (theme == "FluentDark") { - return new string[] { "#6200EE", "#09AF74", "#0076E5", "#CB3587", "#E7910F", "#0364DE", "#66CD15", "#F3A93C", "#107C10", "#C19C00" }; + return new string[] { "#9BB449", "#2A72D5", "#43B786", "#3F579A", "#584EC6", "#E85F9C", "#6E7A89", "#EA6266", "#0B6A0B", "#C19C00" }; } - else + if (theme == "HighContrast") { - return new string[] { "#9BB449", "#2A72D5", "#43B786", "#3F579A", "#584EC6", "#E85F9C", "#6E7A89", "#EA6266", "#0B6A0B", "#C19C00" }; + return new string[] { "#79ECE4", "#E98272", "#DFE6B6", "#C6E773", "#BA98FF", "#FA83C3", "#00C27A", "#43ACEF", "#D681EF", "#D8BC6E" }; } + // Fluent (light) — default. + return new string[] { "#6200EE", "#09AF74", "#0076E5", "#CB3587", "#E7910F", "#0364DE", "#66CD15", "#F3A93C", "#107C10", "#C19C00" }; } /// @@ -1966,14 +1973,16 @@ internal static Rect GetTransform(Rect xAxisRect, Rect yAxisRect, bool invertedA /// The scrollbar theme style. internal static ScrollbarThemeStyle GetScrollbarThemeColor(string theme) { - if(theme != "FluentDark") + if (theme == "FluentDark") { - return GetScrollbarStyle("#F5F5F5", "#F0F0F0", "#FAFAFA", "#FAFAFA", "#424242", "#424242"); + return GetScrollbarStyle("#0A0A0A", "#141414", "#1F1F1F", "#1F1F1F", "#D6D6D6", "#D6D6D6"); } - else + if (theme == "HighContrast") { - return GetScrollbarStyle("#0A0A0A", "#141414", "#1F1F1F", "#1F1F1F", "#D6D6D6", "#D6D6D6"); + return GetScrollbarStyle("#000000", "#000000", "#000000", "#000000", "#FFFF00", "#FFFF00"); } + // Fluent (light) — default. + return GetScrollbarStyle("#F5F5F5", "#F0F0F0", "#FAFAFA", "#FAFAFA", "#424242", "#424242"); } /// diff --git a/tests/Syncfusion.Blazor.Toolkit.BUnitTest/Charts/Chart/ThemeTests/HighContrastThemeTests.razor b/tests/Syncfusion.Blazor.Toolkit.BUnitTest/Charts/Chart/ThemeTests/HighContrastThemeTests.razor new file mode 100644 index 0000000..036ee39 --- /dev/null +++ b/tests/Syncfusion.Blazor.Toolkit.BUnitTest/Charts/Chart/ThemeTests/HighContrastThemeTests.razor @@ -0,0 +1,386 @@ +@using Syncfusion.Blazor.Toolkit.Charts +@using Syncfusion.Blazor.Toolkit.Tests +@using Syncfusion.Blazor.Toolkit.Charts.Internal +@using Syncfusion.Blazor.Toolkit.Internal +@using System.Reflection +@using Bunit +@using Xunit + +@inherits TestComponentBase + +@* + HighContrast theme token coverage. + + Verifies the contract of: + ChartHelper.GetChartThemeStyle(theme) + ChartHelper.GetSeriesColor(theme) + ChartHelper.GetScrollbarThemeColor(theme) + SfChart.Theme = Theme.HighContrast derives a high-contrast _chartThemeStyle. + + Mirror of the existing fixture-based razor tests (e.g. Scatter.razor, Legend.razor). +*@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +@code { + SfFixture hcBackgroundFixture { get; set; } + SfFixture hcTextFixture { get; set; } + SfFixture hcFocusFixture { get; set; } + SfFixture hcGridFixture { get; set; } + SfFixture hcAxisFixture { get; set; } + SfFixture hcTypeFixture { get; set; } + SfFixture fluentFixture { get; set; } + SfFixture fluentDarkFixture { get; set; } + SfFixture hcSeriesPrimaryFixture { get; set; } + SfFixture hcSeriesDistinctFixture { get; set; } + SfFixture fluentSeriesFixture { get; set; } + SfFixture fluentDarkSeriesFixture { get; set; } + SfFixture hcScrollbarFixture { get; set; } + SfFixture fluentDarkScrollbarFixture { get; set; } + SfFixture fluentScrollbarFixture { get; set; } + SfFixture hcSfChartFixture { get; set; } + SfFixture themeSwitchFixture { get; set; } + + const string HighContrast = "HighContrast"; + const string Fluent = "Fluent"; + const string FluentDark = "FluentDark"; + + #region ChartHelper.GetChartThemeStyle + + async Task GetChartThemeStyle_HighContrast_HasBlackBackground(Fixture fixture) + { + await Task.Delay(50); + ChartThemeStyle style = ChartHelper.GetChartThemeStyle(HighContrast); + Assert.Equal("#000000", style.Background); + } + + async Task GetChartThemeStyle_HighContrast_HasWhiteTextTokens(Fixture fixture) + { + await Task.Delay(50); + ChartThemeStyle style = ChartHelper.GetChartThemeStyle(HighContrast); + + // Pure white text tokens: chart title and axis title. + Assert.Equal("#FFFFFF", style.ChartTitle); + Assert.Equal("#FFFFFF", style.AxisTitle); + } + + async Task GetChartThemeStyle_HighContrast_HasYellowFocusTokens(Fixture fixture) + { + await Task.Delay(50); + ChartThemeStyle style = ChartHelper.GetChartThemeStyle(HighContrast); + + // Soft accessibility yellow is the focus / selection accent. + Assert.Equal("#FFD939", style.SelectionCircleStroke); + Assert.Equal("#FFD939", style.TabColor); + } + + async Task GetChartThemeStyle_HighContrast_HasGrayGridLines(Fixture fixture) + { + await Task.Delay(50); + ChartThemeStyle style = ChartHelper.GetChartThemeStyle(HighContrast); + + // On a black background, grid lines use neutral grays. + Assert.Equal("#BFBFBF", style.MajorGridLine); + Assert.Equal("#969696", style.MinorGridLine); + } + + async Task GetChartThemeStyle_HighContrast_HasGrayAxisLabels(Fixture fixture) + { + await Task.Delay(50); + ChartThemeStyle style = ChartHelper.GetChartThemeStyle(HighContrast); + + // Axis labels and legend labels are intentionally the same neutral #969696. + Assert.Equal("#969696", style.AxisLabel); + Assert.Equal("#969696", style.LegendLabel); + } + + async Task GetChartThemeStyle_HighContrast_FontSizing(Fixture fixture) + { + await Task.Delay(50); + ChartThemeStyle style = ChartHelper.GetChartThemeStyle(HighContrast); + + Assert.Equal("16px", style.ChartTitleSize); + Assert.Equal("600", style.ChartTitleFontWeight); + Assert.Equal("14px", style.LegendTextSize); + Assert.Equal("400", style.LegendFontWeight); + Assert.Equal("14px", style.AxisTitleFontSize); + Assert.Equal("600", style.AxisTitleFontWeight); + Assert.Equal("12px", style.AxisLabelFontSize); + Assert.Equal("400", style.AxisLabelFontWeight); + } + + async Task GetChartThemeStyle_Fluent_StillReturnsLightTokens(Fixture fixture) + { + await Task.Delay(50); + ChartThemeStyle style = ChartHelper.GetChartThemeStyle(Fluent); + Assert.Equal("#FFFFFF", style.Background); + // Fluent (light) AxisLabel is the first positional argument — #616161. + Assert.Equal("#616161", style.AxisLabel); + } + + async Task GetChartThemeStyle_FluentDark_StillReturnsDarkTokens(Fixture fixture) + { + await Task.Delay(50); + ChartThemeStyle style = ChartHelper.GetChartThemeStyle(FluentDark); + Assert.Equal("#1c1b1f", style.Background); + // FluentDark AxisLabel is the first positional argument — #ADADAD. + Assert.Equal("#ADADAD", style.AxisLabel); + } + + #endregion + + #region ChartHelper.GetSeriesColor + + async Task GetSeriesColor_HighContrast_ReturnsHighContrastPalette(Fixture fixture) + { + await Task.Delay(50); + string[] palette = ChartHelper.GetSeriesColor(HighContrast); + Assert.NotNull(palette); + Assert.NotEmpty(palette); + // First color is the canonical primary: cyan #79ECE4. + Assert.Equal("#79ECE4", palette[0]); + } + + async Task GetSeriesColor_HighContrast_PaletteHasDistinctColors(Fixture fixture) + { + await Task.Delay(50); + string[] palette = ChartHelper.GetSeriesColor(HighContrast); + Assert.Equal(10, palette.Length); + // The HighContrast palette uses ten distinguishable accent tones. + Assert.Contains("#79ECE4", palette); + Assert.Contains("#E98272", palette); + Assert.Contains("#DFE6B6", palette); + Assert.Contains("#C6E773", palette); + Assert.Contains("#BA98FF", palette); + Assert.Contains("#FA83C3", palette); + Assert.Contains("#00C27A", palette); + Assert.Contains("#43ACEF", palette); + Assert.Contains("#D681EF", palette); + Assert.Contains("#D8BC6E", palette); + } + + async Task GetSeriesColor_Fluent_StillReturnsFluentPalette(Fixture fixture) + { + await Task.Delay(50); + string[] palette = ChartHelper.GetSeriesColor(Fluent); + Assert.Equal("#6200EE", palette[0]); + } + + async Task GetSeriesColor_FluentDark_StillReturnsDarkPalette(Fixture fixture) + { + await Task.Delay(50); + string[] palette = ChartHelper.GetSeriesColor(FluentDark); + Assert.Equal("#9BB449", palette[0]); + } + + #endregion + + #region ChartHelper.GetScrollbarThemeColor + + async Task GetScrollbarThemeColor_HighContrast_ReturnsHighContrastTokens(Fixture fixture) + { + await Task.Delay(50); + ScrollbarThemeStyle style = ChartHelper.GetScrollbarThemeColor(HighContrast); + Assert.Equal("#000000", style.BackRect); + Assert.Equal("#FFFF00", style.Arrow); + Assert.Equal("#FFFF00", style.Grip); + } + + async Task GetScrollbarThemeColor_FluentDark_StillReturnsDarkTokens(Fixture fixture) + { + await Task.Delay(50); + ScrollbarThemeStyle style = ChartHelper.GetScrollbarThemeColor(FluentDark); + Assert.Equal("#0A0A0A", style.BackRect); + Assert.Equal("#D6D6D6", style.Arrow); + } + + async Task GetScrollbarThemeColor_Fluent_StillReturnsLightTokens(Fixture fixture) + { + await Task.Delay(50); + ScrollbarThemeStyle style = ChartHelper.GetScrollbarThemeColor(Fluent); + Assert.Equal("#F5F5F5", style.BackRect); + Assert.Equal("#424242", style.Arrow); + } + + #endregion + + #region SfChart parameter binding + + async Task SfChart_HighContrast_AppliesHighContrastThemeStyle(Fixture fixture) + { + await Task.Delay(200); + IRenderedComponent cut = fixture.GetComponentUnderTest(); + ChartThemeStyle? style = GetInternalChartThemeStyle(cut.Instance); + Assert.NotNull(style); + Assert.Equal("#000000", style!.Background); + // Former #FFFF00 ErrorBar is now neutralized to white in the high-contrast token set. + Assert.Equal("#ffffff", style.ErrorBar); + } + + async Task SfChart_ThemeParameter_UpdatesChartThemeStyle(Fixture fixture) + { + await Task.Delay(200); + IRenderedComponent cut = fixture.GetComponentUnderTest(); + + // First render: default is Fluent → white background. + ChartThemeStyle? initialStyle = GetInternalChartThemeStyle(cut.Instance); + Assert.NotNull(initialStyle); + Assert.Equal("#FFFFFF", initialStyle!.Background); + + // Switch to HighContrast → black background. + cut.SetParametersAndRender(parameters => parameters.Add(p => p.Theme, Theme.HighContrast)); + ChartThemeStyle? highContrastStyle = GetInternalChartThemeStyle(cut.Instance); + Assert.NotNull(highContrastStyle); + Assert.Equal("#000000", highContrastStyle!.Background); + Assert.Equal("#ffffff", highContrastStyle.ErrorBar); + Assert.Equal("#969696", highContrastStyle.AxisLabel); + } + + #endregion + + /// + /// Reads the _chartThemeStyle field on the instance via reflection. + /// + static ChartThemeStyle? GetInternalChartThemeStyle(SfChart chart) + { + FieldInfo? field = typeof(SfChart).GetField( + "_chartThemeStyle", + BindingFlags.Instance | BindingFlags.NonPublic); + Assert.NotNull(field); + return field!.GetValue(chart) as ChartThemeStyle; + } +} \ No newline at end of file