Skip to content

Commit 525caf6

Browse files
committed
[blazor] Added theme switching and moved Index.razor to custom component.
1 parent 1979cf6 commit 525caf6

File tree

8 files changed

+286
-40
lines changed

8 files changed

+286
-40
lines changed
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
@implements IDisposable
2+
3+
@using MyBlazorApp.Models
4+
@using MyBlazorApp.Services
5+
6+
@inject IJSRuntime Js
7+
@inject DashboardDataService DataService
8+
9+
<div class="k-body" style="padding:2em;">
10+
<label>
11+
Select a Telerik theme:
12+
<TelerikDropDownList Data="@ThemeData"
13+
Value="@ThemeSwatchValue"
14+
ValueChanged="@ThemeSwatchValueChanged"
15+
TItem="@ThemeModel"
16+
TValue="@int"
17+
ValueField="@nameof(ThemeModel.Id)"
18+
TextField="@nameof(ThemeModel.FullName)"
19+
Width="240px">
20+
</TelerikDropDownList>
21+
</label>
22+
23+
<TelerikLoader Visible="@LoaderVisible" />
24+
25+
<br/><br/>
26+
27+
<TelerikChart @ref="@ChartRef"
28+
Height="240px">
29+
<ChartSeriesItems>
30+
<ChartSeries Type="ChartSeriesType.Line"
31+
Data="@Series1Data"
32+
Field="@nameof(ChartModel.Revenue)"
33+
CategoryField="@nameof(ChartModel.TimePeriod)"
34+
Name="Product 1">
35+
</ChartSeries>
36+
<ChartSeries Type="ChartSeriesType.Column"
37+
Data="@Series2Data"
38+
Field="@nameof(ChartModel.Revenue)"
39+
CategoryField="@nameof(ChartModel.TimePeriod)"
40+
Name="Product 2">
41+
</ChartSeries>
42+
<ChartSeries Type="ChartSeriesType.Area"
43+
Data="@Series3Data"
44+
Field="@nameof(ChartModel.Revenue)"
45+
CategoryField="@nameof(ChartModel.TimePeriod)"
46+
Name="Product 3">
47+
</ChartSeries>
48+
</ChartSeriesItems>
49+
50+
<ChartCategoryAxes>
51+
<ChartCategoryAxis Type="@ChartCategoryAxisType.Date"></ChartCategoryAxis>
52+
</ChartCategoryAxes>
53+
54+
<ChartValueAxes>
55+
<ChartValueAxis Max="600">
56+
<ChartValueAxisLabels Format="c2" />
57+
</ChartValueAxis>
58+
</ChartValueAxes>
59+
60+
<ChartTitle Text="Telerik Chart"></ChartTitle>
61+
62+
<ChartLegend Position="ChartLegendPosition.Right">
63+
</ChartLegend>
64+
</TelerikChart>
65+
66+
<TelerikGrid Data="@Podcasts"
67+
Sortable="true"
68+
Height="100%"
69+
Pageable="false"
70+
ScrollMode="@GridScrollMode.Scrollable"
71+
SortMode="SortMode.Single"
72+
FilterMode="GridFilterMode.FilterRow"
73+
OnStateInit="@((GridStateEventArgs<PodcastViewModel> args) => OnStateInit(args))">
74+
<GridColumns>
75+
<GridColumn Field="@(nameof(PodcastViewModel.Name))" Title="Podcast Episode" Width="320px" />
76+
<GridColumn Field="@(nameof(PodcastViewModel.Streams))" Width="150px" />
77+
<GridColumn Field="@(nameof(PodcastViewModel.Downloads))" Width="150px" />
78+
</GridColumns>
79+
</TelerikGrid>
80+
</div>
81+
Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
using Microsoft.JSInterop;
2+
using MyBlazorApp.Models;
3+
using System;
4+
using System.Collections.Generic;
5+
using System.Linq;
6+
using System.Threading.Tasks;
7+
using Telerik.Blazor.Components;
8+
using Telerik.DataSource;
9+
10+
namespace MyBlazorApp.Components;
11+
12+
public partial class DemoMain
13+
{
14+
// "Main" is the name of the Razor component that holds this code
15+
private DotNetObjectReference<DemoMain> DotNetRef { get; set; }
16+
private TelerikChart ChartRef { get; set; }
17+
public IEnumerable<PodcastViewModel> Podcasts { get; set; }
18+
private List<ChartModel> Series1Data { get; set; } = new();
19+
private List<ChartModel> Series2Data { get; set; } = new();
20+
private List<ChartModel> Series3Data { get; set; } = new();
21+
private List<ThemeModel> ThemeData { get; set; } = new();
22+
private int ThemeSwatchValue { get; set; } = 1;
23+
private const string ThemeUrlTemplate = "https://unpkg.com/@progress/kendo-theme-{0}@11.0.2/dist/{0}-{1}.css";
24+
private bool LoaderVisible { get; set; }
25+
26+
protected override async Task OnAfterRenderAsync(bool firstRender)
27+
{
28+
if (firstRender)
29+
{
30+
// Ensure HTML is ready
31+
await Task.Delay(1);
32+
33+
// Send the Razor component's reference to the client
34+
// to be able to call NotifyThemeChanged()
35+
await Js.InvokeVoidAsync("saveDotNetRef", DotNetRef);
36+
}
37+
38+
await base.OnAfterRenderAsync(firstRender);
39+
}
40+
41+
protected override async Task OnInitializedAsync()
42+
{
43+
DotNetRef = DotNetObjectReference.Create(this);
44+
45+
PopulateThemes();
46+
47+
await GenerateData();
48+
49+
50+
await base.OnInitializedAsync();
51+
}
52+
53+
private void PopulateThemes()
54+
{
55+
ThemeData.Add(new ThemeModel(1, "Default", "Main"));
56+
ThemeData.Add(new ThemeModel(2, "Default", "Main-Dark"));
57+
ThemeData.Add(new ThemeModel(3, "Default", "Ocean-Blue"));
58+
ThemeData.Add(new ThemeModel(4, "Bootstrap", "Main"));
59+
ThemeData.Add(new ThemeModel(5, "Bootstrap", "Main-Dark"));
60+
ThemeData.Add(new ThemeModel(6, "Material", "Main"));
61+
ThemeData.Add(new ThemeModel(7, "Material", "Main-Dark"));
62+
ThemeData.Add(new ThemeModel(8, "Fluent", "Main"));
63+
}
64+
65+
private async Task GenerateData()
66+
{
67+
Podcasts = await DataService.GetPodcasts();
68+
69+
var now = DateTime.Today;
70+
71+
const int monthsBack = 6;
72+
73+
for (var i = 1; i <= monthsBack; i++)
74+
{
75+
var dateTimeValue = now.AddMonths(-monthsBack + i);
76+
77+
Series1Data.Add(new ChartModel
78+
{
79+
Id = i,
80+
Product = "Product 1",
81+
Revenue = Random.Shared.Next(1, 500),
82+
TimePeriod = dateTimeValue
83+
});
84+
85+
Series2Data.Add(new ChartModel
86+
{
87+
Id = i,
88+
Product = "Product 2",
89+
Revenue = Random.Shared.Next(1, 500),
90+
TimePeriod = dateTimeValue
91+
});
92+
93+
Series3Data.Add(new ChartModel
94+
{
95+
Id = i,
96+
Product = "Product 3",
97+
Revenue = Random.Shared.Next(1, 500),
98+
TimePeriod = dateTimeValue
99+
});
100+
}
101+
}
102+
103+
void OnStateInit(GridStateEventArgs<PodcastViewModel> args)
104+
{
105+
args.GridState = new GridState<PodcastViewModel>
106+
{
107+
SortDescriptors = new List<SortDescriptor>
108+
{
109+
new SortDescriptor { Member = nameof(PodcastViewModel.Streams), SortDirection = ListSortDirection.Descending }
110+
}
111+
};
112+
}
113+
114+
private async Task ThemeSwatchValueChanged(int newValue)
115+
{
116+
// Update DropDownList Value
117+
ThemeSwatchValue = newValue;
118+
119+
LoaderVisible = true;
120+
121+
// Generate new theme URL
122+
var newThemeModel = ThemeData.First(x => x.Id == ThemeSwatchValue);
123+
var newThemeSwatchUrl = string.Format(ThemeUrlTemplate, newThemeModel.Theme.ToLower(), newThemeModel.Swatch.ToLower());
124+
125+
// Change current Telerik theme
126+
await Js.InvokeVoidAsync("changeTelerikTheme", newThemeSwatchUrl);
127+
128+
// The algorithm continues in the NotifyThemeChanged method
129+
}
130+
131+
[JSInvokable("NotifyThemeChanged")]
132+
public void NotifyThemeChanged()
133+
{
134+
// Refresh all Telerik components that use SVG or Canvas rendering (Charts, Gauges, BarCodes, QR Codes)
135+
ChartRef?.Refresh();
136+
137+
LoaderVisible = false;
138+
139+
// This method is not an EventCallback, so you need StateHasChanged() to hide the Loader or make other changes in the UI
140+
StateHasChanged();
141+
}
142+
143+
public void Dispose()
144+
{
145+
DotNetRef?.Dispose();
146+
}
147+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
using System;
2+
3+
namespace MyBlazorApp.Models;
4+
5+
public class ChartModel
6+
{
7+
public int Id { get; set; }
8+
public string Product { get; set; } = string.Empty;
9+
public DateTime TimePeriod { get; set; }
10+
public decimal Revenue { get; set; }
11+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
namespace MyBlazorApp.Models;
2+
3+
public class GridModel
4+
{
5+
public int Id { get; set; }
6+
public string Name { get; set; } = string.Empty;
7+
public decimal Price { get; set; }
8+
public int Quantity { get; set; }
9+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
namespace MyBlazorApp.Models;
2+
3+
public class ThemeModel(int id, string themeName, string swatchName)
4+
{
5+
public int Id { get; set; } = id;
6+
public string Theme { get; set; } = themeName;
7+
public string Swatch { get; set; } = swatchName;
8+
public string FullName => $"{Theme} {Swatch}";
9+
}
Lines changed: 2 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,42 +1,5 @@
11
@page "/"
22

3-
@using MyBlazorApp.Models
4-
@using MyBlazorApp.Services
5-
@using Telerik.DataSource
3+
@using MyBlazorApp.Components
64

7-
@inject DashboardDataService _dataService
8-
9-
<div class="d-flex justify-content-center">
10-
<TelerikGrid Data="@Podcasts"
11-
Sortable="true"
12-
Height="100%"
13-
Pageable="false"
14-
ScrollMode="@GridScrollMode.Scrollable"
15-
OnStateInit="@((GridStateEventArgs<PodcastViewModel> args) => OnStateInit(args))">
16-
<GridColumns>
17-
<GridColumn Field="@( nameof(PodcastViewModel.Name) )" Title="Podcast Episode" Width="320px" />
18-
<GridColumn Field="@( nameof(PodcastViewModel.Streams) )" Width="150px" />
19-
<GridColumn Field="@( nameof(PodcastViewModel.Downloads) )" Width="150px" />
20-
</GridColumns>
21-
</TelerikGrid>
22-
</div>
23-
24-
@code{
25-
public IEnumerable<PodcastViewModel> Podcasts { get; set; }
26-
27-
protected override async Task OnInitializedAsync()
28-
{
29-
Podcasts = await _dataService.GetPodcasts();
30-
}
31-
32-
void OnStateInit(GridStateEventArgs<PodcastViewModel> args)
33-
{
34-
args.GridState = new GridState<PodcastViewModel>()
35-
{
36-
SortDescriptors = new List<SortDescriptor>
37-
{
38-
new SortDescriptor { Member = nameof(PodcastViewModel.Streams), SortDirection = ListSortDirection.Descending }
39-
}
40-
};
41-
}
42-
}
5+
<DemoMain/>

src/Blazor/MyBlazorApp/Pages/_Host.cshtml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
<base href="~/" />
1212
<link rel="stylesheet" href="css/bootstrap/bootstrap.min.css" />
1313
<link href="css/site.css" rel="stylesheet" />
14-
<link id="kendoCss" rel="stylesheet preload" as="style" type="text/css" href="https://unpkg.com/@@progress/kendo-theme-default@10.2.0/dist/all.css" />
14+
<link id="telerik-theme" rel="stylesheet preload" as="style" type="text/css" href="https://unpkg.com/@@progress/kendo-theme-default@11.0.2/dist/all.css" />
1515
</head>
1616
<body>
1717
<component type="typeof(App)" render-mode="Server" />
@@ -30,5 +30,6 @@
3030

3131
<script src="_framework/blazor.server.js"></script>
3232
<script src="_content/Telerik.UI.for.Blazor/js/telerik-blazor.js"></script>
33+
<script src="js/theme-switcher.js"></script>
3334
</body>
3435
</html>
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
var themeChangerDotNetRef;
2+
3+
function saveDotNetRef(dotNetRef) {
4+
themeChangerDotNetRef = dotNetRef;
5+
}
6+
7+
function changeTelerikTheme(newUrl) {
8+
var oldLink = document.getElementById("telerik-theme");
9+
10+
if (newUrl === oldLink.getAttribute("href")) {
11+
return;
12+
}
13+
14+
var newLink = document.createElement("link");
15+
newLink.setAttribute("id", "telerik-theme");
16+
newLink.setAttribute("rel", "stylesheet");
17+
newLink.setAttribute("href", newUrl);
18+
19+
newLink.onload = () => {
20+
oldLink.parentElement.removeChild(oldLink);
21+
themeChangerDotNetRef.invokeMethodAsync("NotifyThemeChanged");
22+
};
23+
24+
document.getElementsByTagName("head")[0].appendChild(newLink);
25+
}

0 commit comments

Comments
 (0)