But such a render time includes all its subcomponents, because of course rendering a component includes rendering all its subcomponents.
It doesn't fit into the model in general to imagine that children always complete rendering before their parent. Steve Sanderson
In other words, a parent component can complete rendering herself before her children do... Depending on how you design and render your components.
However, generally speaking, you can achieve your objective the way you attempted to...
Here's a code sample with a parent and child component, which contains two event call backs that directs the parent component when to pause measuring its render time, and when to renew it (after the child component has been rendered), thus you can separate the render time of the parent component than that of the child component. From my observation, the render time of the child component is almost always the same, perhaps with a tiny variation.
Copy and test. Note: I'm using Blazor Server App without pre-rendering , .Net 5.0
WeatherForecastService.cs
using System.Threading;
public class WeatherForecastService
{
private static readonly string[] Summaries = new[]
{
"Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
};
public Task<WeatherForecast[]> GetForecastAsync(DateTime startDate)
{
Thread.Sleep(7000);
// 2200 ms
//int y = 100;
//for (var x = 0; x < 1000000000; x++)
//{
// y = y + 10;
//}
var rng = new Random();
return Task.FromResult(Enumerable.Range(1, 5).Select(index => new WeatherForecast
{
Date = startDate.AddDays(index),
TemperatureC = rng.Next(-20, 55),
Summary = Summaries[rng.Next(Summaries.Length)]
}).ToArray());
}
public WeatherForecast[] GetForecastAsync2(DateTime startDate)
{
Thread.Sleep(7000);
// 2200 ms
//int y = 100;
//for (var x = 0; x < 1000000000; x++)
//{
// y = y + 10;
//}
var rng = new Random();
return Enumerable.Range(1, 5).Select(index => new WeatherForecast
{
Date = startDate.AddDays(index),
TemperatureC = rng.Next(-20, 55),
Summary = Summaries[rng.Next(Summaries.Length)]
}).ToArray();
}
}
FetchData.razor
@page "/fetchdata"
@using RenderTime.Data
@using System.Diagnostics
@inject WeatherForecastService ForecastService
<h1>Weather forecast</h1>
<p>This component demonstrates fetching data from a service.</p>
@if (forecasts == null)
{
<p><em>Loading...</em></p>
}
else
{
<table class="table">
<thead>
<tr>
<th>Date</th>
<th>Temp. (C)</th>
<th>Temp. (F)</th>
<th>Summary</th>
</tr>
</thead>
<tbody>
@foreach (var forecast in forecasts)
{
<tr>
<td>@forecast.Date.ToShortDateString()</td>
<td>@forecast.TemperatureC</td>
<td>@forecast.TemperatureF</td>
<td>@forecast.Summary</td>
</tr>
}
</tbody>
</table>
}
@code {
private WeatherForecast[] forecasts;
#nullable enable
private Stopwatch? RenderTimer { get; set; } = null;
#nullable disable
[Parameter]
public EventCallback HandlerStart { get; set; }
[Parameter]
public EventCallback<int> HandlerEnd { get; set; }
protected override async Task OnInitializedAsync()
{
RenderTimer = Stopwatch.StartNew();
await HandlerStart.InvokeAsync();
forecasts = await ForecastService.GetForecastAsync(DateTime.Now);
}
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
RenderTimer.Stop();
var renderTime = (int)RenderTimer.ElapsedMilliseconds;
await HandlerEnd.InvokeAsync(renderTime);
}
}
}
Parent.razor
@using System.Diagnostics
<FetchData HandlerStart="StopWatching" HandlerEnd="ProceedWatching" />
@code
{
#nullable enable
private Stopwatch? RenderTimer { get; set; } = null;
#nullable disable
private TimeSpan ts;
protected override void OnInitialized()
{
RenderTimer = Stopwatch.StartNew();
}
private void StopWatching()
{
if (RenderTimer == null) return;
RenderTimer.Stop();
ts = RenderTimer.Elapsed;
Console.WriteLine($"{ts.Milliseconds} before stop watching...");
}
private void ProceedWatching(int childComponentRendertime)
{
// if (RenderTimer == null) return;
Console.WriteLine($"Child component render time: {childComponentRendertime}");
RenderTimer.Start();
}
protected override void OnAfterRender(bool firstRender)
{
RenderTimer.Stop();
Console.WriteLine($"{(int)RenderTimer.ElapsedMilliseconds} ms to render {GetType().Name}");
}
}
Index.razor
@page "/"
<button @onclick="ToggleParent">@ToggleState</button>
@if(collapseParent)
{
@RenderParent()
}
@code
{
private bool collapseParent = false;
private string ToggleState = "Show";
private void ToggleParent()
{
collapseParent = !collapseParent;
ToggleState = collapseParent switch { true => "Hide", false => "Show"};
}
private RenderFragment RenderParent() => builder =>
{
builder.OpenComponent(0, typeof(Parent));
builder.CloseComponent();
};
}