Skip to content

Repository files navigation

Blazor Scheduler — Custom Context Menu for Scheduler Regions

This example adds a DevExpress Blazor Context Menu to the DevExpress Blazor Scheduler. When users right-click within any Scheduler region, the application detects the clicked region and displays a context menu with relevant commands.

Scheduler Region Context Menu Commands
Appointment Edit
All Day Area Switch to Day View (only if ActiveViewType != Day)
Switch to Today
Time Cell Switch to Today
Switch to Day View (only if ActiveViewType != Day)
Date Header Switch to Day View (only if ActiveViewType != Day)
Switch to Today
Day of Week Header Switch to Today
Resource Header Hide Resource (only if visible resource count > 1)
Show All Resources
Time Ruler Toggle Work Time
Toolbar Switch to Day View (if not Day)
Switch to Week View (if not Week)
Switch to Work Week View (if not WorkWeek)
Switch to Month View (if not Month)
Switch to Timeline View (if not Timeline)
Switch to Today

Toolbar menu:

Toolbar Context Menu

Resource header menu:

Resource Header Context Menu

Implementation Details

Detect a Clicked Region and Display the Menu

The application uses a combination of Blazor and JavaScript to detect the clicked region and display the context menu. For appointments, a shared appointment template identifies the clicked appointment. For other regions and elements, a JavaScript module handles the contextmenu event and calls back into .NET to display the menu.

If you need to add a context menu to an element that supports templates, you can use the approach used for appointments. If you need to add a context menu to an element that does not support templates, you can use the same approach as this application uses for other regions.

Appointments

To detect a clicked appointment, Index.razor defines a shared appointmentTemplate object. The template is reused across all Scheduler views (Day, Week, Work Week, Month, and Timeline).

The template's context parameter contains an Appointment property that gets the current appointment. The template handles right-clicks on the appointment (@oncontextmenu event) and calls ShowAppointmentContextMenu(e, context.Appointment).

Index.razor

@{
    RenderFragment<DxSchedulerAppointmentView> appointmentTemplate = context => @<div class="card @context.Label?.BackgroundCssClass">
        <div @oncontextmenu="((e) => ShowAppointmentContextMenu(e, context.Appointment))">
            @context.Appointment.Subject
        </div>
    </div>;
}

Index.razor.cs

private async Task ShowAppointmentContextMenu(MouseEventArgs e, DxSchedulerAppointmentItem appointment) {
    if(ContextMenu is null || appointment is null)
        return;

    ClickedRegion = "Appointment";
    ContextMenuAppointment = appointment;
    await ContextMenu.ShowAsync(e);
}

Other Regions

The example relies on the appointmentContextMenu.js module to process the following Scheduler regions: date header, time cell, resource header, all-day cell, and day-of-week header. The module handles the browser's contextmenu event, determines region by a CSS class, and displays the appropriate menu on the .NET side of the application.

The OnHtmlCellDecoration event handler is used to apply custom CSS classes to Scheduler regions.

Index.razor.cs

private void OnHtmlCellDecoration(SchedulerHtmlCellDecorationEventArgs e) {
    switch(e.CellType) {
        case SchedulerCellType.DateHeader:
            e.CssClass = "custom-date-header";
            break;
        case SchedulerCellType.TimeCell:
            e.CssClass = "custom-time-cell";
            break;
        case SchedulerCellType.ResourceHeader:
            e.CssClass = "custom-resource-header-" + e.Resources.FirstOrDefault()?.Id;
            break;
        case SchedulerCellType.AllDayTimeCell:
            e.CssClass = "custom-all-date-time-cell";
            break;
        case SchedulerCellType.DayOfWeekHeader:
            e.CssClass = "custom-day-of-week-header";
            break;
        case SchedulerCellType.None:
            e.CssClass = "custom-none";
            break;
    }
}

The getRegion method determines an event target, matches region CSS classes, and passes region name (along with an optional resource id and cell start_date) back to the Scheduler component:

appointmentContextMenu.js

export function setup(schedulerElement, dotNetRef) {
    schedulerElement.addEventListener('contextmenu', (e) => {
        e.preventDefault();
        const region = getRegion(e.target, schedulerElement);
        if (!region) return;
        dotNetRef.invokeMethodAsync('ShowAppointmentContextMenu',
            e.clientX, e.clientY, e.pageX, e.pageY, region.name, region.id, region.start_date);
    });
}

When a region is detected, the module calls the [JSInvokable] ShowAppointmentContextMenu method which then sets the ClickedRegion value and displays the menu at the cursor position.

Index.razor.cs

[JSInvokable]
public async Task ShowAppointmentContextMenu(double clientX, double clientY, double pageX, double pageY, string region, int? id, long? startDate) {
    ClickedRegion = region;
    ClickedId = id;
    DayToGo = startDate.HasValue ? DateTimeOffset.FromUnixTimeMilliseconds(startDate.Value).DateTime : null;

    StateHasChanged();

    await (ContextMenu?.ShowAsync(new MouseEventArgs {
        ClientX = clientX,
        ClientY = clientY,
        PageX = pageX,
        PageY = pageY
    }) ?? Task.FromResult(false));
}

Note

The appointmentContextMenu.js module relies on DevExpress internal CSS classes (dxbl-sc-*, dxbl-v-*). These classes may change between release cycles. Review and update them as necessary when/if you upgrade DevExpress-powered Blazor app.

Region-Aware Menu Commands

Index.razor declares a single DxContextMenu used for all regions. Its items are generated dynamically based on the ClickedRegion value. A switch block renders DxContextMenuItem items applicable to a clicked region and the current view. For example, "Open this day in Day View" is hidden when the Day View is active.

Index.razor

<DxContextMenu @ref="@ContextMenu" ItemClick="@OnItemClick">
    <Items>
        <DxContextMenuItem Text="@ClickedRegion" Enabled="false" CssClass="fw-bold"></DxContextMenuItem>
        @switch (ClickedRegion)
        {
            case "Appointment":
                <DxContextMenuItem Text="Edit" Name="Edit" IconUrl="@GetMenuIcon("Edit")"></DxContextMenuItem>
                break;
            case "All Day Area":
                @if (ActiveViewType != SchedulerViewType.Day) {
                    <DxContextMenuItem Text="@OpenDayInDayViewText" Name="SwitchToDayView" IconUrl="@GetMenuIcon("SwitchToDayView")"></DxContextMenuItem>
                }
                <DxContextMenuItem Text="@GoToTodayText" Name="GoToToday" IconUrl="@GetMenuIcon("GoToToday")"></DxContextMenuItem>
                break;
            case "Time Cell":
                <DxContextMenuItem Text="@GoToTodayText" Name="GoToToday" IconUrl="@GetMenuIcon("GoToToday")"></DxContextMenuItem>
                @if (ActiveViewType != SchedulerViewType.Day) {
                    <DxContextMenuItem Text="@OpenDayInDayViewText" Name="SwitchToDayView" IconUrl="@GetMenuIcon("SwitchToDayView")"></DxContextMenuItem>
                }
                break;
            // other regions
        }
    </Items>
</DxContextMenu>

Handle Context Menu Clicks

The OnItemClick event handler processes item clicks based on command Name value:

  • Edit — opens the appointment edit form using ShowAppointmentEditFormAsync.
  • SwitchToDayView / SwitchToWeekView / SwitchToWorkWeekView / SwitchToMonthView — changes ActiveViewType and navigates to the clicked day (if available).
  • GoToToday — resets StartDate to DateTime.Today.
  • HideResource / ShowAllResources — updates the VisibleResources collection bound to VisibleResourcesDataSource.
  • ToggleWorkTime — toggles the ShowWorkTimeOnly option across views.

Index.razor.cs

private async Task OnItemClick(ContextMenuItemClickEventArgs args) {
    switch(args.ItemInfo.Name) {
        case "Edit":
            if(Scheduler is null || ContextMenuAppointment is null)
                break;

            await Scheduler.ShowAppointmentEditFormAsync(false, ContextMenuAppointment);
            break;
        case "GoToToday":
            StartDate = DateTime.Today;
            break;
        case "SwitchToDayView":
            ActiveViewType = SchedulerViewType.Day;
            if(DayToGo is not null)
                StartDate = DayToGo.Value;
            break;
        // other commands
    }

    StateHasChanged();
}

Files to Review

Documentation

Related Examples

Does This Example Address Your Development Requirements/Objectives?

(you will be redirected to DevExpress.com to submit your response)

About

This example adds a DevExpress Blazor Context Menu to the DevExpress Blazor Scheduler. When users right-click within any Scheduler region, the application detects the clicked region and displays a context menu with relevant commands.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages