blazor-404564-components-scheduler-customization-appointment-forms-and-tooltips.md
The DevExpress Blazor Scheduler allows you to customize appointment forms and tooltips. This topic describes default forms and tooltips, how to customize their appearance, and embed predefined/custom layout items and buttons.
Run Demo: Scheduler - Custom Appointment Form
Run Demo: Scheduler - Appointment Tooltip Template
The DevExpress Blazor Scheduler shows a tooltip when a user clicks an appointment. The tooltip’s header contains the following buttons: Show Edit Form, Delete Appointment, Close Appointment, and Restore Occurence (for appointments with changed occurence). The tooltip’s body can display the appointment subject, start date, end date, location, description, and resource (if these properties are specified).
You can customize the tooltip’s body and header.
You can also hide the tooltip (set the ShowAppointmentTooltip property to false).
You can use AppointmentTooltipTemplate and AppointmentTooltipHeaderTemplate properties to customize a tooltip’s body and header. These properties accept a SchedulerAppointmentTooltipInfo object as the context parameter. Use the context parameter to access appointment data.
You can add predefined or custom buttons to appointment tooltip templates.
The following code snippet creates a custom tooltip. The tooltip header displays the appointment’s subject (context.Appointment.Subject), the accepted status if it is true, and predefined buttons. The tooltip body displays the appointment’s subject (context.Appointment.Subject) and resource (context.Resource.Caption).
@using Data
<DxScheduler StartDate="@DateTime.Today"
DataStorage="@DataStorage"
GroupType="SchedulerGroupType.Date"
ShowAppointmentTooltip = "true">
<Views>
<DxSchedulerDayView DayCount="2" ShowWorkTimeOnly="true"/>
</Views>
<AppointmentTooltipHeaderTemplate>
<div class="tooltip-text-header">@context.Appointment.Subject</div>
@if (IsAccepted(context))
{
<div class="my-margin">(Accepted)</div>
}
<DxSchedulerShowAppointmentCompactFormButton></DxSchedulerShowAppointmentCompactFormButton>
<DxSchedulerDeleteAppointmentButton></DxSchedulerDeleteAppointmentButton>
<DxSchedulerCloseAppointmentButton></DxSchedulerCloseAppointmentButton>
</AppointmentTooltipHeaderTemplate>
<AppointmentTooltipTemplate>
<span class="my-align">
@context.Appointment.Subject
</span>
@if (context.Resource != null)
{
<span class="my-align">
(@context.Resource.Caption)
</span>
}
</AppointmentTooltipTemplate>
</DxScheduler>
@code {
DxSchedulerDataStorage DataStorage = new DxSchedulerDataStorage() {
AppointmentsSource = ResourceAppointmentCollection.GetAppointments(),
AppointmentMappings = new DxSchedulerAppointmentMappings() {
Type = "AppointmentType",
Start = "StartDate",
End = "EndDate",
Subject = "Caption",
AllDay = "AllDay",
Location = "Location",
Description = "Description",
LabelId = "Label",
StatusId = "Status",
RecurrenceInfo = "Recurrence",
ResourceId = "ResourceId",
CustomFieldMappings = new List<DxSchedulerCustomFieldMapping> {
new DxSchedulerCustomFieldMapping{ Name = "Accepted", Mapping = "Accepted" }
}
},
ResourcesSource = ResourceAppointmentCollection.GetResourcesForGrouping(),
ResourceMappings = new DxSchedulerResourceMappings() {
Id = "Id",
Caption = "Name",
BackgroundCssClass = "BackgroundCss",
TextCssClass = "TextCss"
}
};
bool IsAccepted(SchedulerAppointmentTooltipInfo tooltipInfo) =>
(bool)tooltipInfo.CustomFields["Accepted"];
}
using System;
namespace Scheduler.Data
{
public class ResourceAppointment {
public ResourceAppointment() { }
public int AppointmentType { get; set; }
public DateTime StartDate { get; set; }
public DateTime EndDate { get; set; }
public string Caption { get; set; }
public string Description { get; set; }
public string Location { get; set; }
public int? Label { get; set; }
public int Status { get; set; }
public bool AllDay { get; set; }
public string Recurrence { get; set; }
public int? ResourceId { get; set; }
public bool Accepted { get; set; }
}
}
using System;
using System.Collections.Generic;
using System.Linq;
namespace Scheduler.Data {
public static partial class ResourceAppointmentCollection {
public static List<ResourceAppointment> GetAppointments() {
DateTime date = DateTime.Now.Date;
var dataSource = new List<ResourceAppointment>() {
new ResourceAppointment {
Accepted = true,
Caption = "Install New Router in Dev Room",
StartDate = date + (new TimeSpan(0, 10, 0, 0)),
EndDate = date + (new TimeSpan(0, 12, 0, 0)),
Status = 1,
ResourceId = 0
},
new ResourceAppointment {
Caption = "Upgrade Personal Computers",
Accepted = false,
StartDate = date + (new TimeSpan(0, 13, 0, 0)),
EndDate = date + (new TimeSpan(0, 14, 30, 0)),
Status = 1,
ResourceId = 0
},
new ResourceAppointment {
Caption = "Website Redesign Plan",
Accepted = false,
StartDate = date + (new TimeSpan(1, 9, 30, 0)),
EndDate = date + (new TimeSpan(1, 11, 30, 0)),
Status = 1,
ResourceId = 0
},
new ResourceAppointment {
Caption = "New Brochures",
Accepted = true,
StartDate = date + (new TimeSpan(1, 13, 30, 0)),
EndDate = date + (new TimeSpan(1, 15, 15, 0)),
Status = 1,
ResourceId = 0
},
new ResourceAppointment {
Caption = "Book Flights to San Fran for Sales Trip",
Accepted = false,
StartDate = date + (new TimeSpan(1, 12, 0, 0)),
EndDate = date + (new TimeSpan(1, 13, 0, 0)),
AllDay = true,
Status = 1,
ResourceId = 0
},
new ResourceAppointment {
Caption = "Approve Personal Computer Upgrade Plan",
Accepted = true,
StartDate = date + (new TimeSpan(0, 10, 0, 0)),
EndDate = date + (new TimeSpan(0, 12, 0, 0)),
Status = 1
},
new ResourceAppointment {
Caption = "Final Budget Review",
Accepted = true,
StartDate = date + (new TimeSpan(0, 13, 0, 0)),
EndDate = date + (new TimeSpan(0, 15, 0, 0)),
Status = 1,
ResourceId = 1
},
new ResourceAppointment {
Caption = "Install New Database",
Accepted = false,
StartDate = date + (new TimeSpan(0, 9, 45, 0)),
EndDate = date + (new TimeSpan(1, 11, 15, 0)),
Status = 1,
ResourceId = 1
},
new ResourceAppointment {
Accepted = true,
Caption = "Approve New Online Marketing Strategy",
StartDate = date + (new TimeSpan(1, 12, 0, 0)),
EndDate = date + (new TimeSpan(1, 14, 0, 0)),
Status = 1,
ResourceId = 1
},
new ResourceAppointment {
Accepted = true,
Caption = "Customer Workshop",
StartDate = date + (new TimeSpan(0, 11, 0, 0)),
EndDate = date + (new TimeSpan(0, 12, 0, 0)),
AllDay = true,
Status = 1,
ResourceId = 2
},
new ResourceAppointment {
Accepted = true,
Caption = "Prepare 2021 Marketing Plan",
StartDate = date + (new TimeSpan(0, 11, 0, 0)),
EndDate = date + (new TimeSpan(0, 13, 30, 0)),
Status = 1,
ResourceId = 2
},
new ResourceAppointment {
Accepted = false,
Caption = "Brochure Design Review",
StartDate = date + (new TimeSpan(0, 14, 0, 0)),
EndDate = date + (new TimeSpan(0, 15, 30, 0)),
Status = 1,
ResourceId = 2
},
new ResourceAppointment {
Accepted = true,
Caption = "Create Icons for Website",
StartDate = date + (new TimeSpan(1, 10, 0, 0)),
EndDate = date + (new TimeSpan(1, 11, 30, 0)),
Status = 1,
ResourceId = 1
},
new ResourceAppointment {
Accepted = true,
Caption = "Launch New Website",
StartDate = date + (new TimeSpan(1, 12, 20, 0)),
EndDate = date + (new TimeSpan(1, 14, 0, 0)),
Status = 1,
ResourceId = 2
},
new ResourceAppointment {
Accepted = false,
Caption = "Upgrade Server Hardware",
StartDate = date + (new TimeSpan(1, 9, 0, 0)),
EndDate = date + (new TimeSpan(1, 12, 0, 0)),
Status = 1,
ResourceId = 2
},
new ResourceAppointment {
Accepted = true,
Caption = "Book Flights to San Fran for Sales Trip",
StartDate = date + (new TimeSpan(0, 14, 0, 0)),
EndDate = date + (new TimeSpan(0, 17, 0, 0)),
Status = 1,
ResourceId = 3
},
new ResourceAppointment {
Accepted = true,
Caption = "Approve New Online Marketing Strategy",
StartDate = date + (new TimeSpan(0, 12, 0, 0)),
EndDate = date + (new TimeSpan(0, 15, 0, 0)),
Status = 1,
ResourceId = 4
}
};
return dataSource;
}
public static List<Resource> GetResourcesForGrouping() {
return GetResources().Take(3).ToList();
}
public static List<Resource> GetResources() {
return new List<Resource>() {
new Resource() { Id=0 , Name="John Heart", GroupId=100, BackgroundCss="dxbl-green-color", TextCss="text-white" },
new Resource() { Id=1 , Name="Samantha Bright", GroupId=101, BackgroundCss="dxbl-orange-color", TextCss="text-white" },
new Resource() { Id=2 , Name="Arthur Miller", GroupId=100, BackgroundCss="dxbl-purple-color", TextCss="text-white" },
new Resource() { Id=3 , Name="Robert Reagan", GroupId=101, BackgroundCss="dxbl-indigo-color", TextCss="text-white" },
new Resource() { Id=4 , Name="Greta Sims", GroupId=100, BackgroundCss="dxbl-red-color", TextCss="text-white" }
};
}
public static List<Resource> GetResourceGroups() {
return new List<Resource>() {
new Resource() { Id=100, Name="Sales and Marketing", IsGroup=true },
new Resource() { Id=101, Name="Engineering", IsGroup=true }
};
}
}
}
namespace Scheduler.Data {
public class Resource {
public int Id { get; set; }
public int? GroupId { get; set; }
public string Name { get; set; }
public bool IsGroup { get; set; }
public string TextCss { get; set; }
public string BackgroundCss { get; set; }
public string ImageFileName => $"employees/{Id + 1}.jpg";
public override bool Equals(object obj) {
Resource resource = obj as Resource;
return resource != null && resource.Id == Id;
}
public override int GetHashCode() {
return Id;
}
}
}
.tooltip-text-header {
margin-right: auto;
text-overflow: ellipsis;
overflow: hidden;
white-space: nowrap;
}
.my-margin {
margin-right: .3em;
}
.my-align {
vertical-align: middle;
}
Run Demo: Scheduler - Appointment Tooltip Template
A Scheduler can show two forms when users create and edit appointments:
Extended Appointment Form
Compact Appointment Form
Use the AppointmentFormMode property to specify which form is available for users.
@using Data
<DxScheduler StartDate="@DateTime.Today"
DataStorage="@DataStorage"
AppointmentFormMode="SchedulerAppointmentFormMode.EditForm">
<DxSchedulerWeekView/>
</DxScheduler>
@code {
DxSchedulerDataStorage DataStorage = new DxSchedulerDataStorage() {
AppointmentsSource = AppointmentCollection.GetAppointments(),
AppointmentMappings = new DxSchedulerAppointmentMappings() {
Type = "AppointmentType",
Start = "StartDate",
End = "EndDate",
Subject = "Caption",
AllDay = "AllDay",
Location = "Location",
Description = "Description",
LabelId = "Label",
StatusId = "Status",
RecurrenceInfo = "Recurrence"
}
};
}
using System;
namespace Scheduler.Data
{
public class Appointment {
public Appointment() { }
public int AppointmentType { get; set; }
public DateTime StartDate { get; set; }
public DateTime EndDate { get; set; }
public string Caption { get; set; }
public string Description { get; set; }
public string Location { get; set; }
public int? Label { get; set; }
public int Status { get; set; }
public bool AllDay { get; set; }
public string Recurrence { get; set; }
public int? ResourceId { get; set; }
public bool Accepted { get; set; }
}
}
using System;
using System.Collections.Generic;
using System.Globalization;
namespace Scheduler.Data
{
public static partial class AppointmentCollection {
public static List<Appointment> GetAppointments()
{
DateTime date = DateTime.Today;
var dataSource = new List<Appointment>() {
new Appointment {
Caption = "Upgrade Personal Computers",
StartDate = date + (new TimeSpan(0, 14, 0, 0)),
EndDate = date + (new TimeSpan(0, 16, 30, 0)),
Label = 1,
Status = 1
},
new Appointment {
Caption = "Install New Router in Dev Room",
StartDate = date + (new TimeSpan(0, 11, 30, 0)),
EndDate = date + (new TimeSpan(0, 13, 30, 0)),
Label = 6,
Status = 1
},
new Appointment {
Caption = "New Brochures",
StartDate = date + (new TimeSpan(1, 15, 00, 0)),
EndDate = date + (new TimeSpan(1, 16, 45, 0)),
Label = 8,
Status = 1
},
new Appointment {
Caption = "Approve Personal Computer Upgrade Plan",
StartDate = date + (new TimeSpan(3, 13, 30, 0)),
EndDate = date + (new TimeSpan(3, 16, 0, 0)),
Label = 1,
Status = 1
},
new Appointment {
Caption = "Customer Workshop",
StartDate = date + (new TimeSpan(4, 11, 0, 0)),
EndDate = date + (new TimeSpan(4, 12, 0, 0)),
AllDay = true,
Label = 8,
Status = 1
},
new Appointment {
Caption = "Upgrade Server Hardware",
StartDate = date + (new TimeSpan(6, 11, 0, 0)),
EndDate = date + (new TimeSpan(6, 13, 30, 0)),
Label = 6,
Status = 1
},
new Appointment {
AppointmentType = 1,
Caption = "Daily Meeting",
StartDate = date + (new TimeSpan(0, 9, 00, 0)),
EndDate = date + (new TimeSpan(0, 10, 00, 0)),
Label = 10,
Status = 1,
Recurrence = string.Format("<RecurrenceInfo Type=\"0\" Start=\"{0}\" Range=\"1\" OccurrenceCount=\"10\" Frequency =\"1\" Id=\"72e3db8f-cdb6-4aaa-afe1-e3c6b80ce995\"/>", ToString(date + (new TimeSpan(0, 9, 00, 0))))
}
};
return dataSource;
}
private static string ToString(DateTime dateTime) {
return dateTime.ToString(CultureInfo.InvariantCulture);
}
}
}
You can change the Repeat item list in default appointment forms. Refer to Specify Available Recurrence Modes.
You can also create a custom appointment form.
Use the following properties to create a custom appointment edit form:
You can use predefined and custom layout items to construct the layout in the same way as when you use the DxFormLayout component.
You can also add predefined and custom buttons to appointment forms and their headers.
Run Demo: Scheduler - Custom Appointment Form
View Example: Customize Appointment Forms
You can add the following predefined layout items to set up the layout. They correspond to the items of the default appointment form.
<DxScheduler StartDate="@DateTime.Today"
DataStorage="@DataStorage"
ActiveViewType="SchedulerViewType.WorkWeek">
<Views>
<DxSchedulerWorkWeekView VisibleTime="@(new DxSchedulerTimeSpanRange(TimeSpan.FromHours(8),
TimeSpan.FromHours(19)))">
@*...*@
</DxSchedulerWorkWeekView>
</Views>
<AppointmentFormLayout >
<DxSchedulerSubjectFormLayoutItem/>
<DxSchedulerAllDayFormLayoutItem/>
<DxSchedulerStartDateFormLayoutItem/>
<DxSchedulerStartTimeFormLayoutItem/>
<DxSchedulerEndDateFormLayoutItem/>
<DxSchedulerEndTimeFormLayoutItem/>
<DxSchedulerRepeatFormLayoutItem/>
<DxSchedulerLocationFormLayoutItem/>
<DxSchedulerLabelFormLayoutItem/>
<DxSchedulerStatusFormLayoutItem/>
<DxSchedulerResourceFormLayoutItem/>
<DxSchedulerDescriptionFormLayoutItem/>
</AppointmentFormLayout>
</DxScheduler>
You can customize settings of these predefined items. For instance, show or hide an icon, assign a custom icon, change a caption, and much more. Refer to an item’s member list for additional information.
If predefined items do not suit your requirements, you can use a custom layout item. Add a DxSchedulerCustomFormLayoutItem to the form layout and use its Template property to define item content. For example, you can display an editor for a custom appointment property.
AppointmentFormLayout and AppointmentCompactFormLayout accept a SchedulerAppointmentFormInfo object as the Context parameter. You can use this parameter in the layout item template to obtain information about appointment settings.
If appointments contain custom properties, implement a SchedulerAppointmentFormInfo descendant and declare the corresponding properties in the class. Handle the AppointmentFormShowing event and assign a custom descendant instance to the event’s FormInfo argument.
<DxScheduler StartDate="@DateTime.Today"
DataStorage="@DataStorage"
ActiveViewType="SchedulerViewType.WorkWeek"
AppointmentFormShowing="OnAppointmentFormShowing">
@*...*@
<AppointmentFormLayout Context="formInfo">
@*...*@
<DxSchedulerCustomFormLayoutItem ColSpanMd="12">
<Template>
<div class="my-style">
<DxCheckBox @bind-Checked="@(((CustomAppointmentFormInfo)formInfo).IsAccepted)"
Alignment="CheckBoxContentAlignment.Right">Accept</DxCheckBox>
</div>
</Template>
</DxSchedulerCustomFormLayoutItem>
</AppointmentFormLayout>
</DxScheduler>
@code {
DxScheduler scheduler { get; set; }
public class CustomAppointmentFormInfo : SchedulerAppointmentFormInfo {
public CustomAppointmentFormInfo(DxSchedulerAppointmentItem AppointmentItem,
DxSchedulerDataStorage DataStorage, DxScheduler scheduler) : base(AppointmentItem, DataStorage, scheduler) { }
public bool IsAccepted {
get { return (bool)CustomFields["IsAccepted"]; }
set { CustomFields["IsAccepted"] = value; }
}
}
void OnAppointmentFormShowing(SchedulerAppointmentFormEventArgs args) {
args.FormInfo = new CustomAppointmentFormInfo(args.Appointment, DataStorage, scheduler);
}
DxSchedulerDataStorage DataStorage = new DxSchedulerDataStorage() {
AppointmentsSource = AppointmentCollection.GetAppointments(),
AppointmentMappings = new DxSchedulerAppointmentMappings() {
Type = "AppointmentType",
Start = "StartDate",
End = "EndDate",
Subject = "Caption",
AllDay = "AllDay",
Location = "Location",
Description = "Description",
LabelId = "Label",
StatusId = "Status",
RecurrenceInfo = "Recurrence"
}
};
// ...
}
.my-style {
margin-left: auto;
margin-top: 14px;
}
You can add the following predefined buttons to appointment forms, tooltips and their headers:
<DxScheduler StartDate="@DateTime.Today"
DataStorage="@DataStorage"
ActiveViewType="SchedulerViewType.WorkWeek">
<Views>
<DxSchedulerWorkWeekView VisibleTime="@(new DxSchedulerTimeSpanRange(TimeSpan.FromHours(8),
TimeSpan.FromHours(19)))">
@*...*@
</DxSchedulerWorkWeekView>
</Views>
<AppointmentFormHeaderTemplate>
<DxSchedulerSaveAppointmentChangesButton/>
<DxSchedulerDeleteAppointmentButton/>
<DxSchedulerDiscardAppointmentChangesButton/>
</AppointmentFormHeaderTemplate>
</DxScheduler>
You can customize settings of these predefined buttons. For instance, you can show or hide an icon and change a button’s text or tooltip.
If predefined buttons do not suit your requirements, you can create a custom button. Add a DxButton to the form layout or form header, and handle its Click event. You can use the following methods in the handler:
The following code snippet creates a button that closes the edit form.
<DxScheduler StartDate="@DateTime.Today"
DataStorage="@DataStorage"
@ref="Scheduler">
<Views>
<DxSchedulerWeekView ShowWorkTimeOnly="false"
TimeIndicatorVisibility="SchedulerTimeIndicatorVisibility.Never"
TimeScale="@(new TimeSpan(0,15,0))"
WorkTime="@(new DxSchedulerTimeSpanRange(TimeSpan.FromHours(9), TimeSpan.FromHours(18)))"
VisibleTime="@(new DxSchedulerTimeSpanRange(TimeSpan.FromHours(8), TimeSpan.FromHours(19)))">
</DxSchedulerWeekView>
</Views>
<AppointmentFormHeaderTemplate>
<div class="popup-text-header">@context.Subject</div>
<DxButton Click="@(() => Scheduler.ClosePopupAsync())"
Text="Close"
IconCssClass="btn-icon-close"
RenderStyle="ButtonRenderStyle.None"
CssClass="custom-button">
</DxButton>
</AppointmentFormHeaderTemplate>
</DxScheduler>
@code {
DxScheduler Scheduler { get; set; }
}
Follow the steps below to enable appointment form validation:
true.Important
You should not rely on form validation alone to secure your Blazor-powered app. Form validation is designed to improve usability. A threat actor can bypass validation and send malicious data to the server. To minimize security related threats/risks, you must validate user input using multiple strategies. Refer to the following topic for additional information: Validate User Input.
@using System.ComponentModel.DataAnnotations
<DxScheduler StartDate="@DateTime.Today"
DataStorage="@DataStorage"
ActiveViewType="SchedulerViewType.WorkWeek"
AppointmentFormShowing="OnAppointmentFormShowing"
ValidateEditForm="true">
@*...*@
<AppointmentFormLayout>
@*...*@
<DxSchedulerCustomFormLayoutItem ColSpanMd="12">
<Template>
<ValidationSummary />
</Template>
</DxSchedulerCustomFormLayoutItem>
</AppointmentFormLayout>
</DxScheduler>
@code {
DxScheduler scheduler { get; set; }
public class CustomAppointmentFormInfo : SchedulerAppointmentFormInfo {
public CustomAppointmentFormInfo(DxSchedulerAppointmentItem AppointmentItem,
DxSchedulerDataStorage DataStorage, DxScheduler scheduler) : base(AppointmentItem, DataStorage, scheduler) { }
[Required]
public override string Subject {
get { return base.Subject; }
set { base.Subject = value; }
}
// ...
}
void OnAppointmentFormShowing(SchedulerAppointmentFormEventArgs args) {
args.FormInfo = new CustomAppointmentFormInfo(args.Appointment, DataStorage, scheduler);
}
// ...
}
The following example demonstrates how to:
IsAccepted property’s editor, the second item shows validation summary.true to enable appointment form validation.Extended Appointment Form
Compact Appointment Form
@using System.ComponentModel.DataAnnotations
<DxScheduler StartDate="@DateTime.Today"
DataStorage="@DataStorage"
ActiveViewType="SchedulerViewType.WorkWeek"
AppointmentFormShowing="OnAppointmentFormShowing"
ValidateEditForm="true"
CssClass="mw-1100">
<Views>
<DxSchedulerWorkWeekView VisibleTime="@(new DxSchedulerTimeSpanRange(TimeSpan.FromHours(8),
TimeSpan.FromHours(19)))">
<HorizontalAppointmentTemplate>
<div class="demo-sc-apt @((bool)context.CustomFields["IsAccepted"] ?
"demo-sc-accepted " : "")">
<div class="card demo-apt-bg dxbl-purple-color"></div>
<div class="card shadow-sm p-1 demo-sc-apt-content text-white">
@context.Appointment.Subject
</div>
</div>
</HorizontalAppointmentTemplate>
<VerticalAppointmentTemplate>
<div class="shadow-sm demo-sc-apt @((bool)context.CustomFields["IsAccepted"] ?
"demo-sc-accepted" : "")">
<div class="card demo-apt-bg dxbl-purple-color"></div>
<div class="card demo-sc-apt-content text-white">
<div class="card demo-sc-status-container">
<div class="card demo-apt-status dxbl-purple-color"></div>
</div>
<div class="demo-apt-subject">
@context.Appointment.Subject
</div>
</div>
</div>
</VerticalAppointmentTemplate>
</DxSchedulerWorkWeekView>
</Views>
<AppointmentFormLayout Context="formInfo">
<DxSchedulerSubjectFormLayoutItem/>
<DxSchedulerAllDayFormLayoutItem/>
<DxSchedulerStartDateFormLayoutItem/>
<DxSchedulerStartTimeFormLayoutItem/>
<DxSchedulerEndDateFormLayoutItem/>
<DxSchedulerEndTimeFormLayoutItem/>
<DxSchedulerLocationFormLayoutItem/>
<DxSchedulerDescriptionFormLayoutItem/>
<DxSchedulerCustomFormLayoutItem ColSpanMd="12">
<Template>
<div class="my-style">
<DxCheckBox @bind-Checked="@(((CustomAppointmentFormInfo)formInfo).IsAccepted)"
Alignment="CheckBoxContentAlignment.Right">Accept</DxCheckBox>
</div>
</Template>
</DxSchedulerCustomFormLayoutItem>
<DxSchedulerCustomFormLayoutItem ColSpanMd="12">
<Template>
<ValidationSummary />
</Template>
</DxSchedulerCustomFormLayoutItem>
</AppointmentFormLayout>
<AppointmentCompactFormLayout Context="formInfo">
<DxSchedulerSubjectFormLayoutItem></DxSchedulerSubjectFormLayoutItem>
<DxSchedulerAllDayFormLayoutItem></DxSchedulerAllDayFormLayoutItem>
<DxSchedulerStartDateFormLayoutItem></DxSchedulerStartDateFormLayoutItem>
<DxSchedulerStartTimeFormLayoutItem></DxSchedulerStartTimeFormLayoutItem>
<DxSchedulerEndDateFormLayoutItem></DxSchedulerEndDateFormLayoutItem>
<DxSchedulerEndTimeFormLayoutItem></DxSchedulerEndTimeFormLayoutItem>
<DxSchedulerCustomFormLayoutItem ColSpanMd="12">
<Template>
<div class="my-style">
<DxCheckBox @bind-Checked="@(((CustomAppointmentFormInfo)formInfo).IsAccepted)"
Alignment="CheckBoxContentAlignment.Right">Accept</DxCheckBox>
</div>
</Template>
</DxSchedulerCustomFormLayoutItem>
<DxSchedulerCustomFormLayoutItem ColSpanMd="12">
<Template>
<ValidationSummary />
</Template>
</DxSchedulerCustomFormLayoutItem>
</AppointmentCompactFormLayout>
</DxScheduler>
@code {
public class CustomAppointmentFormInfo : SchedulerAppointmentFormInfo {
public CustomAppointmentFormInfo(DxSchedulerAppointmentItem AppointmentItem, DxSchedulerDataStorage DataStorage)
: base(AppointmentItem, DataStorage) { }
[Required]
public override string Subject {
get { return base.Subject; }
set { base.Subject = value; }
}
public bool IsAccepted {
get { return (bool)CustomFields["IsAccepted"]; }
set { CustomFields["IsAccepted"] = value; }
}
}
void OnAppointmentFormShowing(SchedulerAppointmentFormEventArgs args) {
args.FormInfo = new CustomAppointmentFormInfo(args.Appointment, DataStorage);
}
DxSchedulerDataStorage DataStorage = new DxSchedulerDataStorage() {
AppointmentsSource = AppointmentCollection.GetAppointments(),
AppointmentMappings = new DxSchedulerAppointmentMappings() {
Type = "AppointmentType",
Start = "StartDate",
End = "EndDate",
Subject = "Caption",
AllDay = "AllDay",
Location = "Location",
Description = "Description",
CustomFieldMappings = new List<DxSchedulerCustomFieldMapping> {
new DxSchedulerCustomFieldMapping { Name = "IsAccepted", Mapping = "Accepted" }
}
}
};
}
public class Appointment {
public Appointment() {}
public int AppointmentType { get; set; }
public DateTime StartDate { get; set; }
public DateTime EndDate { get; set; }
public string Caption { get; set; }
public string Description { get; set; }
public string Location { get; set; }
public int Label { get; set; }
public int Status { get; set; }
public bool AllDay { get; set; }
public string Recurrence { get; set; }
public bool Accepted { get; set; }
}
public static partial class AppointmentCollection {
public static List<Appointment> GetAppointments() {
DateTime date = DateTime.Today;
var dataSource = new List<Appointment>() {
new Appointment {
Caption = "Install New Router in Dev Room",
StartDate = date + (new TimeSpan(0, 10, 0, 0)),
EndDate = date + (new TimeSpan(0, 12, 30, 0)),
Label = 6,
Status = 4
},
new Appointment {
Caption = "Upgrade Personal Computers",
StartDate = date + (new TimeSpan(0, 13, 0, 0)),
EndDate = date + (new TimeSpan(0, 15, 30, 0)),
Label = 1,
Status = 4
},
new Appointment {
Caption = "Website Redesign Plan",
StartDate = date + (new TimeSpan(1, 9, 30, 0)),
EndDate = date + (new TimeSpan(1, 12, 0, 0)),
Label = 1,
Status = 1,
Accepted = true
},
new Appointment {
Caption = "New Brochures",
StartDate = date + (new TimeSpan(1, 13, 30, 0)),
EndDate = date + (new TimeSpan(1, 15, 15, 0)),
Label = 8,
Status = 2,
Accepted = true
},
// ...
};
return dataSource;
}
}
.my-style {
margin-left: auto;
margin-top: 14px;
}
Run Demo: Scheduler - Custom Fields and Appointment Form
When a user creates or edits a recurring appointment and selects a value other than Never in the Appointment form’s Repeat section, the Recurrence form appears. This form has a different layout for each rule type: Daily, Weekly, Monthly, Yearly. When a user fills in form fields and clicks Save , field values are saved to the DxSchedulerRecurrenceInfo object.
In the Recurrence form, you can change the list of repeat end items and the list of week days available for Repeat Monthly and Repeat Yearly appointments. To do this, handle the Scheduler’s AppointmentFormShowing event, use the RecurrenceFormInfo property to get information about the Recurrence form, modify RepeatEndItems and WeekDayItems properties.
The following code snippet does the following:
Defines two items in the Repeat end list: End after, End by. The list displays items in the same order as in code.
Removes the Weekend item from the Week day list.
<DxScheduler DataStorage="@DataStorage"
AppointmentFormMode="SchedulerAppointmentFormMode.EditForm"
AppointmentFormShowing="OnAppointmentFormShowing">
<DxSchedulerWeekView ShowWorkTimeOnly="true" />
</DxScheduler>
@code {
DxSchedulerDataStorage DataStorage = new DxSchedulerDataStorage() {
AppointmentsSource = AppointmentCollection.GetAppointments(),
AppointmentMappings = new DxSchedulerAppointmentMappings() {
Start = "StartDate",
End = "EndDate",
Subject = "Caption",
LabelId = "Label",
StatusId = "Status"
}
};
void OnAppointmentFormShowing(SchedulerAppointmentFormEventArgs args) {
args.FormInfo.RecurrenceFormInfo.RepeatEndItems = new List<SchedulerRecurrenceRange>() {
SchedulerRecurrenceRange.OccurrenceCount,
SchedulerRecurrenceRange.EndByDate
};
args.FormInfo.RecurrenceFormInfo.WeekDayItems.Remove(SchedulerWeekDays.WeekendDays);
}
}
public class Appointment {
public Appointment() {}
public int AppointmentType { get; set; }
public DateTime StartDate { get; set; }
public DateTime EndDate { get; set; }
public string Caption { get; set; }
public string Description { get; set; }
public string Location { get; set; }
public int Label { get; set; }
public int Status { get; set; }
public bool AllDay { get; set; }
public string Recurrence { get; set; }
public bool Accepted { get; set; }
}
public static partial class AppointmentCollection {
public static List<Appointment> GetAppointments() {
DateTime date = DateTime.Today;
var dataSource = new List<Appointment>() {
new Appointment {
Caption = "Install New Router in Dev Room",
StartDate = date + (new TimeSpan(0, 10, 0, 0)),
EndDate = date + (new TimeSpan(0, 12, 30, 0)),
Label = 6,
Status = 4
},
new Appointment {
Caption = "Upgrade Personal Computers",
StartDate = date + (new TimeSpan(0, 13, 0, 0)),
EndDate = date + (new TimeSpan(0, 15, 30, 0)),
Label = 1,
Status = 4
},
new Appointment {
Caption = "Website Redesign Plan",
StartDate = date + (new TimeSpan(1, 9, 30, 0)),
EndDate = date + (new TimeSpan(1, 12, 0, 0)),
Label = 1,
Status = 1,
Accepted = true
},
new Appointment {
Caption = "New Brochures",
StartDate = date + (new TimeSpan(1, 13, 30, 0)),
EndDate = date + (new TimeSpan(1, 15, 15, 0)),
Label = 8,
Status = 2,
Accepted = true
},
// ...
};
return dataSource;
}
}
You can also customize the Repeat field in default appointment forms. Refer to Specify Available Recurrence Modes.
The DevExpress Blazor Scheduler component allows you to handle events that fire when users open/close tooltips and edit forms. You can cancel operations or execute additional logic.
| Event | Description |
|---|---|
| AppointmentTooltipShowing | Fires before an appointment tooltip is shown. |
| AppointmentTooltipShown | Fires after an appointment tooltip is shown. |
| AppointmentTooltipClosing | Fires before an appointment tooltip is closed. |
| AppointmentTooltipClosed | Fires after an appointment tooltip is closed. |
| AppointmentFormShowing | Fires before the appointment form is shown. |
| AppointmentFormShown | Fires after an appointment form is shown. |
| AppointmentFormClosing | Fires before the appointment form is closed. |
| AppointmentFormClosed | Fires after the appointment form is closed. |