Backend API
        
    @model DevExtreme.NETCore.Demos.ViewModels.RecurringAppointmentsViewModel
@using DevExtreme.NETCore.Demos.Models;
@{
    var cellContextMenuItems = new[] {
        new ContextMenuItem { Text = "New Appointment", OnItemClick = new JS("createAppointment") },
        new ContextMenuItem { Text = "New Recurring Appointment", OnItemClick = new JS("createRecurringAppointment") },
        new ContextMenuItem { Text = "Group by Room/Ungroup", BeginGroup = true, OnItemClick = new JS("groupCell") },
        new ContextMenuItem { Text = "Go to Today", OnItemClick = new JS("showCurrentDate") }
    };
    var resourcesData = Model.Resources.Select(r => new
    {
        Id = r.Id,
        Text = r.Text,
        Color = r.Color,
    });
    var appointmentContextMenuItems = new[] {
        new ContextMenuItem { Text = "Open", OnItemClick = new JS("showAppointment") },
        new ContextMenuItem { Text = "Delete", OnItemClick = new JS("deleteAppointment") },
        new ContextMenuItem { Text = "Repeat Weekly", BeginGroup = true, OnItemClick = new JS("repeatAppointmentWeekly") },
        new ContextMenuItem { Text = "Set Room", BeginGroup = true, Disabled = true }
    }.Concat(
        resourcesData.Select(r => new ContextMenuItem {
            Id = r.Id,
            Text = r.Text,
            Color = r.Color,
            OnItemClick = new JS("setResource")
        })
    );
}
@(Html.DevExtreme().Scheduler()
    .ID("scheduler")
    .DataSource(Model.Appointments)
    .TimeZone("America/Los_Angeles")
    .Views(new[] { SchedulerViewType.Day, SchedulerViewType.Month })
    .CurrentView(SchedulerViewType.Month)
    .CurrentDate(new DateTime(2020, 11, 25))
    .RecurrenceEditMode(SchedulerRecurrenceEditMode.Series)
    .StartDayHour(9)
    .Resources(res => { res.Add()
        .FieldExpr("RoomId")
        .ValueExpr("Id")
        .ColorExpr("Color")
        .DisplayExpr("Text")
        .Label("Room")
        .DataSource(resourcesData);
    })
    .OnCellContextMenu("onCellContextMenu")
    .OnAppointmentContextMenu("onAppointmentContextMenu")
    .Height(730)
    .TextExpr("Text")
    .StartDateExpr("StartDate")
    .EndDateExpr("EndDate")
    .RecurrenceRuleExpr("RecurrenceRule")
    .RecurrenceExceptionExpr("RecurrenceException")
)
@(Html.DevExtreme().ContextMenu()
    .ID("context-menu")
    .Target(".dx-scheduler-appointment")
    .Disabled(true)
    .Width(200)
    .ItemTemplate(new JS("contextMenuItemTemplate"))
)
<script>
    var appointmentClassName = ".dx-scheduler-appointment";
    var cellClassName = ".dx-scheduler-date-table-cell";
    function onAppointmentContextMenu(e) {
        var contextMenuInstance = $("#context-menu").dxContextMenu("instance");
        contextMenuInstance.option({
            dataSource: @Html.Raw(System.Text.Json.JsonSerializer.Serialize(appointmentContextMenuItems)),
            target: appointmentClassName,
            onItemClick: onItemClick(e),
            disabled: false,
        });
    }
    function onCellContextMenu(e) {
        var contextMenuInstance = $("#context-menu").dxContextMenu("instance");
        contextMenuInstance.option({
            dataSource: @Html.Raw(System.Text.Json.JsonSerializer.Serialize(cellContextMenuItems)),
            target: cellClassName,
            onItemClick: onItemClick(e),
            disabled: false,
        });
    }
    function onItemClick(contextMenuEvent) {
        return function (e) {
            e.itemData.onItemClick(contextMenuEvent, e);
        }
    }
    function createAppointment(e) {
        e.component.showAppointmentPopup({
            StartDate: e.cellData.startDate
        }, true);
    };
    function createRecurringAppointment(e) {
        e.component.showAppointmentPopup({
            StartDate: e.cellData.startDate,
            RecurrenceRule: "FREQ=DAILY"
        }, true);
    };
    function groupCell(e) {
        var scheduler = e.component;
        if(scheduler.option("groups")) {
            scheduler.option({ crossScrollingEnabled: false, groups: undefined });
        } else {
            scheduler.option({ crossScrollingEnabled: true, groups: ["RoomId"] });
        }
    };
    function showCurrentDate(e) {
        e.component.option("currentDate", new Date());
    };
    function showAppointment(e) {
        e.component.showAppointmentPopup(e.appointmentData);
    };
    function deleteAppointment(e) {
        e.component.deleteAppointment(e.appointmentData);
    };
    function repeatAppointmentWeekly(e) {
        var itemData = e.appointmentData;
        e.component.updateAppointment(itemData, $.extend(itemData, {
            StartDate: e.targetedAppointmentData.StartDate,
            RecurrenceRule: "FREQ=WEEKLY"
        }));
    };
    function setResource(e, clickEvent) {
        var itemData = e.appointmentData;
        e.component.updateAppointment(itemData, $.extend(itemData, {
            RoomId: [clickEvent.itemData.Id]
        }));
    };
    function contextMenuItemTemplate(itemData) {
        var $template = $('<div></div>');
        if(itemData.Color) {
            $template.append("<div class='appointment-badge' style='background-color:" + itemData.Color + ";'></div>");
        }
        $template.append(itemData.Text);
        return $template;
    }
</script>
        
        using Microsoft.AspNetCore.Mvc;
using DevExtreme.NETCore.Demos.Models.SampleData;
using DevExtreme.NETCore.Demos.ViewModels;
namespace DevExtreme.NETCore.Demos.Controllers {
    public class SchedulerController : Controller {
        public ActionResult ContextMenu() {
            return View(new RecurringAppointmentsViewModel {
                Appointments = SampleData.RecurringAppointments,
                Resources = SampleData.RecurringAppointmentsResources,
            });
        }
    }
}
        
        using DevExtreme.NETCore.Demos.Models.SampleData;
using System.Text.Json.Serialization;
using DevExtreme.AspNet.Mvc;
namespace DevExtreme.NETCore.Demos.Models {
    public class ContextMenuItem : RecurringAppointmentsResource {
        [JsonPropertyName("onItemClick")]
        public JS OnItemClick { get; set; } = new JS("function() {}");
        [JsonPropertyName("beginGroup")]
        public bool BeginGroup { get; set; } = false;
        [JsonPropertyName("disabled")]
        public bool Disabled { get; set; } = false;
    }
}
        
        using System;
namespace DevExtreme.NETCore.Demos.Models {
    public class RecurringAppointment : Appointment {
        public int RoomId { get; set; }
    }
}
        
        using System;
using System.Collections.Generic;
using System.Linq;
namespace DevExtreme.NETCore.Demos.Models.SampleData {
    public partial class SampleData {
        public static readonly IEnumerable<RecurringAppointment> RecurringAppointments = new[] {
            new RecurringAppointment {
                Text = "Watercolor Landscape",
                RoomId = 1,
                StartDate = "2020-11-01T17:30:00.000Z",
                EndDate = "2020-11-01T19:00:00.000Z",
                RecurrenceRule = "FREQ=WEEKLY;BYDAY=MO,TH;COUNT=10"
            },
            new RecurringAppointment {
                Text = "Oil Painting for Beginners",
                RoomId = 2,
                StartDate = "2020-11-01T17:30:00.000Z",
                EndDate = "2020-11-01T19:00:00.000Z",
                RecurrenceRule = "FREQ=WEEKLY;BYDAY=SU,WE;COUNT=10"
            },
            new RecurringAppointment {
                Text = "Testing",
                RoomId = 3,
                StartDate = "2020-11-01T20:00:00.000Z",
                EndDate = "2020-11-01T21:00:00.000Z",
                RecurrenceRule = "FREQ=WEEKLY;BYDAY=SU;WKST=TU;INTERVAL=2;COUNT=2"
            },
            new RecurringAppointment {
                Text = "Meeting of Instructors",
                RoomId = 4,
                StartDate = "2020-11-01T17:00:00.000Z",
                EndDate = "2020-11-01T17:15:00.000Z",
                RecurrenceRule = "FREQ=DAILY;BYDAY=TU;UNTIL=20201203"
            },
            new RecurringAppointment {
                Text = "Recruiting students",
                RoomId = 5,
                StartDate = "2020-10-24T18:00:00.000Z",
                EndDate = "2020-10-24T19:00:00.000Z",
                RecurrenceRule = "FREQ=YEARLY;BYWEEKNO=50;WKST=SU",
                RecurrenceException = "20201212T190000Z"
            },
            new RecurringAppointment {
                Text = "Final exams",
                RoomId = 3,
                StartDate = "2020-10-24T20:00:00.000Z",
                EndDate = "2020-10-24T21:35:00.000Z",
                RecurrenceRule = "FREQ=YEARLY;BYWEEKNO=51;BYDAY=WE,TH"
            },
            new RecurringAppointment {
                Text = "Monthly Planning",
                RoomId = 4,
                StartDate = "2020-11-24T22:30:00.000Z",
                EndDate = "2020-11-24T23:45:00.000Z",
                RecurrenceRule = "FREQ=MONTHLY;BYMONTHDAY=28;COUNT=1"
            },
            new RecurringAppointment {
                Text = "Open Day",
                RoomId = 5,
                StartDate = "2020-11-01T17:30:00.000Z",
                EndDate = "2020-11-01T21:00:00.000Z",
                RecurrenceRule = "FREQ=YEARLY;BYYEARDAY=333"
            }
        };
    }
}
        
        namespace DevExtreme.NETCore.Demos.Models.SampleData {
    public class RecurringAppointmentsResource {
        public int Id { get; set; }
        public string Text { get; set; }
        public string Color { get; set; }
    }
}
        
        using System;
using System.Collections.Generic;
using System.Linq;
namespace DevExtreme.NETCore.Demos.Models.SampleData {
    public partial class SampleData {
        public static readonly IEnumerable<RecurringAppointmentsResource> RecurringAppointmentsResources = new[] {
            new RecurringAppointmentsResource {
                Id = 1,
                Text = "Room 101",
                Color = "#bbd806"
            },
            new RecurringAppointmentsResource {
                Id = 2,
                Text = "Room 102",
                Color = "#f34c8a"
            },
            new RecurringAppointmentsResource {
                Id = 3,
                Text = "Room 103",
                Color = "#ae7fcc"
            },
            new RecurringAppointmentsResource {
                Id = 4,
                Text = "Meeting room",
                Color = "#ff8817"
            },
            new RecurringAppointmentsResource {
                Id = 5,
                Text = "Conference hall",
                Color = "#03bb92"
            }
        };
    }
}
        
        .dx-menu-item-content span {
    margin-right: 5px;
}
.dx-menu-item-has-submenu .dx-icon-spinright {
    position: absolute;
    top: 7px;
    right: 2px;
}
.appointment-badge {
    text-align: center;
    float: left;
    margin-right: 12px;
    color: white;
    width: 18px;
    height: 18px;
    font-size: 19.5px;
    border-radius: 18px;
    margin-top: 2px;
}