New to Telerik UI for Blazor? Download free 30-day trial

Prevent Notification Hiding on Hover

Environment

Product Notification for Blazor

Description

This KB article answers the following questions:

  • Is it possible to stop a notification from closing on mouseover?
  • How to keep a notification from hiding automatically when the user moves their mouse over it?
  • How to persist a visible notification on the screen when the user hovers it?
  • How to prevent the hiding of a notification when the mouse cursor is over it?

Solution

  1. Show() notifications that don't close automatically. To achieve this, set CloseAfter to 0 in the NotificationModel.
  2. Define a collection that will store the currently open NotificationModel instances and the DateTime values that determine when they will be hidden.
  3. Implement a System.Timers.Timer that will Hide() notifications according to the times from the previous step.
  4. Define a Notification Template.
  5. Wrap the template content in a <div> with @onmouseenter and @onmouseleave directives. The event handlers must enable or disable the hiding of that specific notification instance by the Timer.

Keep notifications visible on hover

@implements IDisposable

@using System.Timers

<h2>Prevent Notification Hide on Hover</h2>

<TelerikButton OnClick="@ShowNotification">Show Notification</TelerikButton>

<TelerikNotification @ref="@NotificationRef"
                     HorizontalPosition="@NotificationHorizontalPosition.Right"
                     VerticalPosition="@NotificationVerticalPosition.Top">
    <Template>
        <div @onmouseenter="@( () => DisableNotificationHide(context) )"
             @onmouseleave="@( () => EnableNotificationHide(context) )"
             style="padding:1em;">
            Notification Text: @context.Text
        </div>
    </Template>
</TelerikNotification>

@if (!string.IsNullOrEmpty(NotificationToKeep))
{
    <p><strong>Will not hide: @NotificationToKeep</strong></p>
}

@code {
    private TelerikNotification NotificationRef { get; set; } = null!;

    #region Notification Show and Hide Logic

    private Dictionary<Guid, NotificationLog> OpenNotifications { get; set; } = new Dictionary<Guid, NotificationLog>();

    public class NotificationLog
    {
        public DateTime CloseTime { get; set; } = DateTime.Now.AddSeconds(5);

        public bool ShouldHide { get; set; } = true;

        public NotificationModel Model { get; set; } = new NotificationModel()
        {
            Closable = false,
            CloseAfter = 0
        };
    }

    private void ShowNotification()
    {
        var guid = Guid.NewGuid();

        var notificationLog = new NotificationLog();
        notificationLog.Model.Text = $"{(char)Rnd.Next(65, 91)}{(char)Rnd.Next(65, 91)}{(char)Rnd.Next(65, 91)} " +
            $"{Rnd.Next(0, 10)}{Rnd.Next(0, 10)}{Rnd.Next(0, 10)}";
        notificationLog.Model.ThemeColor = NotificationThemeColors[Rnd.Next(0, NotificationThemeColors.Count)];

        NotificationRef.Show(notificationLog.Model);
        OpenNotifications.Add(guid, notificationLog);

        Timer.Start();
    }

    private void CloseExpiredNotifications()
    {
        var notificationsToClose = new List<Guid>();
        var now = DateTime.Now;

        foreach (var item in OpenNotifications)
        {
            if (item.Value.CloseTime < now && item.Value.ShouldHide)
            {
                notificationsToClose.Add(item.Key);
            }
        }

        foreach (var guid in notificationsToClose)
        {
            var notificationInstance = OpenNotifications[guid].Model;
            NotificationRef.Hide(notificationInstance);
            OpenNotifications.Remove(guid);
        }

        if (!OpenNotifications.Any())
        {
            Timer.Stop();
        }
    }

    private void DisableNotificationHide(NotificationModel model)
    {
        var log = OpenNotifications.First(x => x.Value.Model == model);
        log.Value.ShouldHide = false;
    }

    private void EnableNotificationHide(NotificationModel model)
    {
        var log = OpenNotifications.First(x => x.Value.Model == model);
        log.Value.CloseTime = DateTime.Now.AddSeconds(3);
        log.Value.ShouldHide = true;
    }

    #endregion Notification Show and Hide Logic

    #region Timer Related

    private Timer Timer { get; set; } = new Timer(1000);

    private void OnTimerElapsed(object? source, ElapsedEventArgs e)
    {
        InvokeAsync(CloseExpiredNotifications);

        // Call StateHasChanged only if necessary.
        //InvokeAsync(StateHasChanged);
    }

    protected override void OnInitialized()
    {
        Timer.Elapsed -= OnTimerElapsed;
        Timer.Elapsed += OnTimerElapsed;
    }

    public void Dispose()
    {
        Timer?.Stop();
        Timer?.Close();
    }

    #endregion Timer Related

    #region Example Helpers

    private Random Rnd { get; set; } = new Random();

    private string? NotificationToKeep => OpenNotifications.FirstOrDefault(x => !x.Value.ShouldHide).Value?.Model.Text;

    private List<string> NotificationThemeColors { get; set; } = new List<string>() {
        ThemeConstants.Notification.ThemeColor.Primary,
        ThemeConstants.Notification.ThemeColor.Secondary,
        ThemeConstants.Notification.ThemeColor.Tertiary,
        ThemeConstants.Notification.ThemeColor.Success,
        ThemeConstants.Notification.ThemeColor.Warning,
        ThemeConstants.Notification.ThemeColor.Error,
        ThemeConstants.Notification.ThemeColor.Info,
        ThemeConstants.Notification.ThemeColor.Dark,
        ThemeConstants.Notification.ThemeColor.Light,
        ThemeConstants.Notification.ThemeColor.Inverse
    };

    #endregion Example Helpers
}

Notes

  • Visible notifications can move on the screen when other notifications are hidden. As a result, a notification may no longer be under the mouse cursor.
  • You can set Closable to true in the NotificationModel and show notifications that have a close button. However, the close button is not part of the Notification <Template> and hovering it will not prevent the notification from hiding.
  • Be aware of thread safety when changing the page UI with a Timer.

See Also

In this article