@model IEnumerable<DevExtreme.NETCore.Demos.Models.DisableDatesAppointment>
@(Html.DevExtreme().Scheduler()
.ID("scheduler")
.DataSource(Model)
.TextExpr("Text")
.StartDateExpr("StartDate")
.EndDateExpr("EndDate")
.Views(new[] { SchedulerViewType.WorkWeek, SchedulerViewType.Month })
.CurrentView(SchedulerViewType.WorkWeek)
.CurrentDate(new DateTime(2021, 4, 27))
.FirstDayOfWeek(FirstDayOfWeek.Sunday)
.StartDayHour(9)
.EndDayHour(19)
.ShowAllDayPanel(false)
.Height(730)
.DataCellTemplate(new JS("dataCellTemplate"))
.DateCellTemplate(new JS("dateCellTemplate"))
.TimeCellTemplate(new JS("timeCellTemplate"))
.OnContentReady("onContentReady")
.OnAppointmentFormOpening("onAppointmentFormOpening")
.OnAppointmentAdding("onAppointmentAdding")
.OnAppointmentUpdating("onAppointmentUpdating")
)
<script>
var dinnerTime = { from: 12, to: 13 };
var holidays = [
new Date(2021, 3, 29),
new Date(2021, 5, 6)
];
var ariaDescription = () => {
const disabledDates = holidays
.filter((date) => !isWeekend(date))
.map((date) => new Date(date).toLocaleDateString('en-US', {
weekday: 'long',
year: 'numeric',
month: 'long',
day: 'numeric',
})
);
if (disabledDates?.length === 1) {
return `${disabledDates} is a disabled date`;
}
if (disabledDates?.length > 1) {
return `${disabledDates.join(', ')} are disabled dates`;
}
};
function dataCellTemplate(itemData, itemIndex, itemElement) {
var date = itemData.startDate;
var isDisabled = isHoliday(date) || isWeekend(date);
var element = $('<div />');
if (isDisabled) {
element.addClass('disable-date');
} else if (isDinner(date)) {
element.addClass('dinner');
}
return itemElement.append(element);
}
function dateCellTemplate(itemData, itemIndex, itemElement) {
var element = $('<div>' + itemData.text + '</div>');
if (isWeekend(itemData.date)) {
element.addClass('disable-date');
}
return itemElement.append(element);
}
function timeCellTemplate(itemData, itemIndex, itemElement) {
var element = $('<div>' + itemData.text + '</div>');
var date = itemData.date;
if (isDinner(date)) {
element.addClass('dinner');
}
if (hasCoffeeCupIcon(date)) {
element.append('<div class="cafe" />');
}
return itemElement.append(element);
}
function onContentReady(e) {
setComponentAria(e.component?.$element());
}
function onAppointmentFormOpening(e) {
var startDate = new Date(e.appointmentData.StartDate);
if(!isValidAppointmentDate(startDate)) {
e.cancel = true;
notifyDisableDate();
}
applyDisableDatesToDateEditors(e.form);
}
function onAppointmentAdding(e) {
if(!isValidAppointment(e.component, e.appointmentData)) {
e.cancel = true;
notifyDisableDate();
}
}
function onAppointmentUpdating(e) {
if(!isValidAppointment(e.component, e.newData)) {
e.cancel = true;
notifyDisableDate();
}
}
function notifyDisableDate() {
DevExpress.ui.notify("This date is disabled", "warning", 500);
}
function isValidAppointment(component, appointmentData) {
var startDate = new Date(appointmentData.StartDate);
var endDate = new Date(appointmentData.EndDate);
var cellDuration = component.option('cellDuration');
return isValidAppointmentInterval(startDate, endDate, cellDuration);
}
function isValidAppointmentInterval(startDate, endDate, cellDuration) {
var edgeEndDate = new Date(endDate.getTime() - 1);
if (!isValidAppointmentDate(edgeEndDate)) {
return false;
}
var durationInMs = cellDuration * 60 * 1000;
var date = startDate;
while (date <= endDate) {
if (!isValidAppointmentDate(date)) {
return false;
}
var newDateTime = date.getTime() + durationInMs - 1;
date.setTime(newDateTime);
}
return true;
}
function isValidAppointmentDate(date) {
return !isHoliday(date) && !isDinner(date) && !isWeekend(date);
}
function isHoliday(date) {
var localeDate = date.toLocaleDateString();
return holidays.filter(function(holiday) {
return holiday.toLocaleDateString() === localeDate;
}).length > 0;
}
function isWeekend(date) {
var day = date.getDay();
return day === 0 || day === 6;
}
function isDinner(date) {
var hours = date.getHours();
return hours >= dinnerTime.from && hours < dinnerTime.to;
}
function hasCoffeeCupIcon(date) {
var hours = date.getHours();
var minutes = date.getMinutes();
return hours === dinnerTime.from && minutes === 0;
}
function applyDisableDatesToDateEditors(form) {
var startDateEditor = form.getEditor('StartDate');
startDateEditor.option('disabledDates', holidays);
var endDateEditor = form.getEditor('EndDate');
endDateEditor.option('disabledDates', holidays);
}
function setComponentAria(element) {
const prevAria = element?.attr('aria-label') || '';
element?.attr('aria-label', `${prevAria} ${ariaDescription()}`);
}
</script>
using DevExtreme.NETCore.Demos.Models.SampleData;
using DevExtreme.NETCore.Demos.ViewModels;
using Microsoft.AspNetCore.Mvc;
using System;
using System.Collections.Generic;
using System.Linq;
namespace DevExtreme.NETCore.Demos.Controllers {
public class SchedulerController : Controller {
public ActionResult CellTemplates() {
return View(SampleData.AppointmentsWorkHours);
}
}
}
using System;
using System.Text.Json.Serialization;
namespace DevExtreme.NETCore.Demos.Models {
public class Appointment {
[JsonPropertyName("AppointmentId")]
public int AppointmentId { get; set; }
[JsonPropertyName("Text")]
public string Text { get; set; }
[JsonPropertyName("Description")]
public string Description { get; set; }
[JsonPropertyName("StartDate")]
public string StartDate { get; set; }
[JsonPropertyName("EndDate")]
public string EndDate { get; set; }
[JsonPropertyName("AllDay")]
public bool AllDay { get; set; }
[JsonPropertyName("RecurrenceRule")]
public string RecurrenceRule { get; set; }
[JsonPropertyName("RecurrenceException")]
public string RecurrenceException { get; set; }
}
public class DisableDatesAppointment {
[JsonPropertyName("AppointmentId")]
public int AppointmentId { get; set; }
[JsonPropertyName("Text")]
public string Text { get; set; }
[JsonPropertyName("Description")]
public string Description { get; set; }
[JsonPropertyName("StartDate")]
public DateTime StartDate { get; set; }
[JsonPropertyName("EndDate")]
public DateTime EndDate { get; set; }
[JsonPropertyName("AllDay")]
public bool AllDay { get; set; }
[JsonPropertyName("RecurrenceRule")]
public string RecurrenceRule { get; set; }
[JsonPropertyName("RecurrenceException")]
public string RecurrenceException { 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<DisableDatesAppointment> AppointmentsWorkHours = new[] {
new DisableDatesAppointment {
Text = "Website Re-Design Plan",
StartDate = new DateTime(2021, 4, 26, 9, 30, 0),
EndDate = new DateTime(2021, 4, 26, 11, 30, 0)
},
new DisableDatesAppointment {
Text = "Install New Router in Dev Room",
StartDate = new DateTime(2021, 4, 26, 13, 0, 0),
EndDate = new DateTime(2021, 4, 26, 14, 0, 0)
},
new DisableDatesAppointment {
Text = "Approve Personal Computer Upgrade Plan",
StartDate = new DateTime(2021, 4, 27, 10, 0, 0),
EndDate = new DateTime(2021, 4, 27, 11, 0, 0)
},
new DisableDatesAppointment {
Text = "Final Budget Review",
StartDate = new DateTime(2021, 4, 27, 13, 30, 0),
EndDate = new DateTime(2021, 4, 27, 15, 0, 0)
},
new DisableDatesAppointment {
Text = "New Brochures",
StartDate = new DateTime(2021, 4, 26, 15, 0, 0),
EndDate = new DateTime(2021, 4, 26, 16, 15, 0)
},
new DisableDatesAppointment {
Text = "Install New Database",
StartDate = new DateTime(2021, 4, 28, 9, 45, 0),
EndDate = new DateTime(2021, 4, 28, 12, 0, 0)
},
new DisableDatesAppointment {
Text = "Approve New Online Marketing Strategy",
StartDate = new DateTime(2021, 4, 28, 14, 30, 0),
EndDate = new DateTime(2021, 4, 28, 16, 30, 0)
},
new DisableDatesAppointment {
Text = "Upgrade Personal Computers",
StartDate = new DateTime(2021, 4, 27, 15, 30, 0),
EndDate = new DateTime(2021, 4, 27, 16, 45, 0)
},
new DisableDatesAppointment {
Text = "Prepare 2021 Marketing Plan",
StartDate = new DateTime(2021, 5, 3, 13, 0, 0),
EndDate = new DateTime(2021, 5, 3, 15, 0, 0)
},
new DisableDatesAppointment {
Text = "Brochure Design Review",
StartDate = new DateTime(2021, 5, 4, 15, 30, 0),
EndDate = new DateTime(2021, 5, 5, 0, 0, 0)
},
new DisableDatesAppointment {
Text = "Create Icons for Website",
StartDate = new DateTime(2021, 4, 30, 10, 0, 0),
EndDate = new DateTime(2021, 4, 30, 12, 0, 0)
},
new DisableDatesAppointment {
Text = "Upgrade Server Hardware",
StartDate = new DateTime(2021, 4, 30, 16, 30, 0),
EndDate = new DateTime(2021, 4, 30, 18, 0, 0)
},
new DisableDatesAppointment {
Text = "Submit New Website Design",
StartDate = new DateTime(2021, 5, 5, 10, 0, 0),
EndDate = new DateTime(2021, 5, 5, 11, 30, 0)
},
new DisableDatesAppointment {
Text = "Launch New Website",
StartDate = new DateTime(2021, 4, 30, 14, 30, 0),
EndDate = new DateTime(2021, 4, 30, 16, 10, 0)
}
};
}
}
@-moz-document url-prefix() {
.dx-scheduler-work-space-month .dx-scheduler-date-table-cell {
position: relative;
}
.dx-scheduler-work-space-month .dx-scheduler-date-table-cell .disable-date {
position: absolute;
width: 100%;
height: 100%;
}
}
.disable-date,
.dinner {
height: 100%;
width: 100%;
}
.disable-date {
background-image:
repeating-linear-gradient(
135deg,
rgba(247, 234, 224, 1),
rgba(247, 234, 224, 1) 4px,
transparent 4px,
transparent 9px
);
color: rgba(178, 74, 0, 1);
}
.dx-scheduler-header-panel-cell .disable-date {
display: flex;
flex-direction: column;
justify-content: center;
}
.dx-theme-fluent .dx-scheduler-header-panel-cell .disable-date,
.dx-theme-material .dx-scheduler-header-panel-cell .disable-date {
flex-direction: row;
align-items: flex-end;
justify-content: initial;
}
.dinner {
background: #FBF1EB;
}
.dx-scheduler-time-panel-cell .dinner {
color: #C25100;
font-weight: 400;
background: transparent;
}
.dx-draggable {
cursor: auto;
}
td.dx-scheduler-time-panel-cell .dinner .cafe {
height: 200%;
width: 100%;
left: 50%;
-webkit-mask: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 0 24 24" width="24"><path d="M0 0h24v24H0V0z" fill="none"/><path d="M20 3H4v10c0 2.21 1.79 4 4 4h6c2.21 0 4-1.79 4-4v-3h2c1.11 0 2-.9 2-2V5c0-1.11-.89-2-2-2zm0 5h-2V5h2v3zM4 19h16v2H4z"/></svg>');
-webkit-mask-repeat: no-repeat;
-webkit-mask-position-y: 50%;
-webkit-mask-position-x: 100%;
margin-top: -4px;
background-color: #C25100;
}
.dx-scheduler-date-table-cell {
padding: 0;
opacity: 1;
}
@media all and (-ms-high-contrast:none)
{
td.dx-scheduler-time-panel-cell .dinner .cafe {
background-color: transparent;
}
}