Discussed in #6762
Originally posted by MBNSoftware August 24, 2026
Hello,
here is a small component that shows and/or hide something after some delay, in seconds.
It can be used to show an alert on initial loading of a page and remove it automatically after some time. Or show a component or group of component after some delay.
Usage (test.razor) :
@*Waits 3 seconds before displaying the alert and hides it after 5 seconds. Calls DisplayChanged when the display changes *@
<TimeoutBlock InitialDelay="3" HideTimeout="5" OnDisplayChanged="DisplayChanged">
<Alert Color="Color.Primary" @bind-Visible="@AlertVisible" Flex="Flex.JustifyContent.Between" >
Close me now or I will disappear in 5 seconds !
<CloseButton AutoClose="true" />
</Alert>
</TimeoutBlock>
@*Displays the alert immediately and hides it after 5 seconds *@
<TimeoutBlock HideTimeout="5">
<Alert Color="Color.Primary" @bind-Visible="@AlertVisible" Flex="Flex.JustifyContent.Between">
Close me now or I will disappear in 5 seconds !
<CloseButton AutoClose="true" />
</Alert>
</TimeoutBlock>
@*Displays the alert immediately and don't close it automatically *@
<TimeoutBlock >
<Alert Color="Color.Primary" @bind-Visible="@AlertVisible" Flex="Flex.JustifyContent.Between">
Close me !
<CloseButton AutoClose="true" />
</Alert>
</TimeoutBlock>
@*Waits for 3 seconds before displaying the alert and don't close it automatically *@
<TimeoutBlock InitialDelay="3">
<Alert Color="Color.Primary" @bind-Visible="@AlertVisible" Flex="Flex.JustifyContent.Between">
Close me !
<CloseButton AutoClose="true" />
</Alert>
</TimeoutBlock>
Usage (test.razor.cs) :
private static async Task DisplayChanged(bool isVisible)
{
await Console.Out.WriteLineAsync($"{DateTime.Now} : Test page TimeoutBlock display changed: {isVisible}");
}
TimeoutBlock.razor :
<Div Display="@(_isVisible ? Display.Block : Display.None)">
@ChildContent
</Div>
TimeoutBlock.razor.cs :
using Microsoft.AspNetCore.Components;
namespace your.favorite.namespace;
/// <summary>
/// Displays child content for a limited time and optionally waits before showing it.
/// </summary>
public partial class TimeoutBlock : IDisposable
{
private bool _isVisible;
private CancellationTokenSource? _cts;
/// <summary>
/// Gets or sets the content rendered inside the timeout block.
/// </summary>
[Parameter]
public RenderFragment? ChildContent { get; set; }
/// <summary>
/// Gets or sets the delay in seconds before the block is hidden automatically.
/// A value of <c>0</c> means the block is never hidden automatically.
/// </summary>
[Parameter]
public int HideTimeout { get; set; } = 0; // Délai avant masquage (en secondes). 0 => jamais masqué automatiquement.
/// <summary>
/// Gets or sets the initial delay in seconds before the block becomes visible.
/// </summary>
[Parameter]
public int InitialDelay { get; set; } = 0; // Délai avant affichage initial (en secondes)
/// <summary>
/// Occurs whenever the visibility state changes.
/// </summary>
/// <remarks>
/// The callback receives the new visibility value: <c>true</c> when visible and <c>false</c> when hidden.
/// </remarks>
[Parameter]
public EventCallback<bool> OnDisplayChanged { get; set; }
protected override async Task OnInitializedAsync()
{
await base.OnInitializedAsync();
_isVisible = InitialDelay == 0; // Afficher immédiatement si InitialDelay = 0
await SetVisibilityAsync(_isVisible);
}
protected override async Task OnAfterRenderAsync(bool firstRender)
{
await base.OnAfterRenderAsync(firstRender);
if (firstRender)
{
await StartInitialDelayAsync();
}
}
private async Task SetVisibilityAsync(bool visible)
{
if (_isVisible == visible)
{
return;
}
_isVisible = visible;
await InvokeAsync(StateHasChanged);
if (OnDisplayChanged.HasDelegate)
{
await OnDisplayChanged.InvokeAsync(_isVisible);
}
}
private async Task StartInitialDelayAsync()
{
if (InitialDelay <= 0)
{
await SetVisibilityAsync(true);
await StartTimeoutAsync();
return;
}
_cts = new CancellationTokenSource();
try
{
await Task.Delay(TimeSpan.FromSeconds(InitialDelay), _cts.Token);
await SetVisibilityAsync(true);
await StartTimeoutAsync();
}
catch (TaskCanceledException)
{
// Ignorer si le délai est annulé
}
}
private async Task StartTimeoutAsync()
{
if (HideTimeout <= 0) return;
_cts = new CancellationTokenSource();
try
{
await Task.Delay(TimeSpan.FromSeconds(HideTimeout), _cts.Token);
await SetVisibilityAsync(false);
}
catch (TaskCanceledException)
{
// Ignorer si le délai est annulé
}
}
protected override bool ShouldRender()
{
return ChildContent != null;
}
/// <summary>
/// Shows the block and starts the hide timeout if configured.
/// </summary>
/// <returns>A task that completes when the visibility change is processed.</returns>
public async Task ShowAsync()
{
_cts?.Cancel();
await SetVisibilityAsync(true);
await StartTimeoutAsync();
}
/// <summary>
/// Hides the block immediately and cancels the pending auto-hide timer.
/// </summary>
/// <returns>A task that completes when the visibility change is processed.</returns>
public async Task HideAsync()
{
_cts?.Cancel();
await SetVisibilityAsync(false);
}
/// <summary>
/// Releases the resources used by this component, including the active cancellation token.
/// </summary>
public void Dispose()
{
_cts?.Cancel();
_cts?.Dispose();
}
}
It's really a small component but I use it quite often so I thought I may share it if it can be of any use for someone else.
I'm not very good at coming up with the right names, so please bear with me if it does not sound good...
Christophe
Discussed in #6762
Originally posted by MBNSoftware August 24, 2026
Hello,
here is a small component that shows and/or hide something after some delay, in seconds.
It can be used to show an alert on initial loading of a page and remove it automatically after some time. Or show a component or group of component after some delay.
Usage (test.razor) :
Usage (test.razor.cs) :
TimeoutBlock.razor :
TimeoutBlock.razor.cs :
It's really a small component but I use it quite often so I thought I may share it if it can be of any use for someone else.
I'm not very good at coming up with the right names, so please bear with me if it does not sound good...
Christophe