The Calendar shows Dataverse appointments the way Outlook does — and tasks, and any other table the server registers — by day, work week, week or month, or as a week's list. Its toolbar moves between ranges and layouts, a busy day collapses into + more, and appointments at the same time sit side by side. It reads the rows of the range on screen as you move to it, through the same query and security as a grid, and shows their days and times in the user's time zone, as every date on the portal is shown. It comes as an add-on package on each stack, drawn in your Fluent UI theme.
<Calendar regarding={{ tableName: 'account', id: accountId }} /><Calendar Regarding="@(new TableRecordReference("account", accountId))" />The appointments about Schmeler and Sons, the account the account detail page demo shows. Regarding keeps the calendar to the appointments about one record.
Without Regarding, the calendar shows every row the user can read of the tables it shows. It reads the tables the server registers for calendars — notes and every activity table, and any other registered with ForCalendars — each dated by its own column: an appointment by its start and end, a task, phone call, letter or fax by its due date. Appointments and tasks show at first; the others the user may read are offered in the toolbar's Show menu, switched off. DefaultTables (React: defaultTables) chooses the ones shown at first, and Tables (React: tables) the ones it may show at all. The calendar adds its range, and the regarding record, as conditions of its own, so a view can narrow what it shows but never widen it.
<Calendar defaultTables={['appointment', 'task', 'phonecall']} /><Calendar DefaultTables="@(["appointment", "task", "phonecall"])" />The calendar lists only the appointments the user may read, and lets them move one only where they may change it. A portal usually gives contacts the appointments they are invited to: RowsInvolvingContact on requiredattendees and optionalattendees reads them through the appointment's party lists, without making them the contact's own.
// Contacts read the appointments they are invited to, as a required or an optional attendee.
builder.Services.AddTableAccess("appointment", t => t
.ForSignedInUsers()
.RowsInvolvingContact("requiredattendees", "optionalattendees")
.Allow(TableSecurityPermission.Read)
.ReadingAllColumns());
// Every appointment the user can read reaches the portal: being invited already says it is for them.
builder.Services.Configure<SecurityOptions>(options =>
options.PortalVisibility.UnfilteredActivities.Add("appointment"));
Like other activities, an appointment reaches the portal only when its description starts with *WEB*, unless its table is in SecurityOptions.PortalVisibility.UnfilteredActivities. A calendar portal adds appointment there, as above, so staff book appointments in Dynamics and Outlook as they always do.
The portal's application user needs read on appointments in Dataverse as well. Dataverse grants every activity type through one set of privileges, so appointments are covered by the read on activities the Power Portals Pro Application User role already holds.
An occurrence of a recurring appointment is shown like any other appointment, with the repeat icon beside its subject. Clicking one asks whether to open that occurrence or the whole series; moving or resizing one changes that occurrence alone. The calendar shows the occurrences Dataverse has written: Dataverse writes those of a series ahead of time, as far as the environment's expansion window — twelve months by default — and the later ones as time passes, so a series that never ends shows a year of occurrences at a time.
An appointment is drawn by how it shows its time, as Outlook draws it: busy as a tint with a bar beside it, free with the bar alone, tentative striped and out of office in purple, read from its status reason. A completed appointment shows as busy, a canceled one as free with its subject struck through. In a month, where an appointment with a time is a dot beside it, and in the agenda's rows, only the color shows. Appointments you provide take ShowAs and Canceled (React: showAs and canceled), and each is drawn with a class — ppp-calendar-appointment-free, -busy, -tentative, -out-of-office and -canceled — to restyle it by.
In an Editable calendar that loads its own appointments, selecting a time opens a new appointment over it, about the Regarding record, and clicking an appointment opens its details as a form, saved with the same table, row and column security as any record. Moving or resizing a row saves it — its start, its end and whether it takes whole days — where the user may change the row and those columns and it isn't completed, canceled or inactive; a row whose table keeps one date, such as a task's due date, moves but doesn't resize, and one dated by when it was created stays put. Clicking a row of another table opens its form: the columns its registration shows, or your own form for the table, a MultiTableForm inside the calendar (React: forms). An appointment's details and another table's form open in a dialog in the center, or, with FormLocation (React: formLocation), in a side panel on the left or the right. Its attendees are edited with PartyListEdit, which ColumnEdit also uses for every party list — an email's To, From and Cc among them. As the user types, it suggests the contacts, accounts and users they may read and link to (Read and Append To) that have an email address, a table without both never searched; picking one adds it as a chip. Where the environment allows messages to unresolved email recipients — or AllowUnresolvedEmailAddresses says so — an email address that matches no record is added on its own with Enter. An appointment's Regarding offers the tables it can point to whose records the user may read and link to; RegardingTables (React: regardingTables) narrows them to the ones you name. Handle OnRangeSelect to create appointments your own way instead, and SaveChange (React: onAppointmentChange) to save moves your own way.
<Calendar regarding={account} editable regardingTables={['account', 'contact']} />
{/* Anywhere a party list is edited */}
<PartyListEdit columnName="to" allowUnresolvedEmailAddresses /><Calendar Regarding="@account"
Editable="true"
RegardingTables="@(["account", "contact"])" />
<!-- Anywhere a party list is edited -->
<PartyListEdit ColumnName="to" AllowUnresolvedEmailAddresses="true" />In an Editable calendar, an appointment's form offers Repeat: daily or on weekdays, on days of the week, monthly or yearly — on the date or on the weekday, such as the second Tuesday — every so many, and ending never, after a number of times or on a date. Dataverse lays the occurrences out in the user's time zone, so a meeting at ten stays at ten across a change of clocks. A series opens as a form of its own, where how it repeats can be changed, and an occurrence's Delete offers deleting it or the whole series. Repeat is offered to users who may create recurringappointmentmaster records — and, for an appointment that already exists, change and delete it, since it becomes the series. Grant the series as you grant appointments, and give the portal's application user Read on Connection in Dataverse: making an appointment a series reads its connections, and Dataverse refuses the save without it.
// A series is a record of its own table; its occurrences are appointments.
builder.Services.AddTableAccess("recurringappointmentmaster", t => t
.ForSignedInUsers()
.RowsOwnedByContact("requiredattendees")
.Allow(TableSecurityPermission.Read | TableSecurityPermission.Create
| TableSecurityPermission.Write | TableSecurityPermission.Delete)
.ReadingAllColumns()
.WritingAllColumns());
builder.Services.Configure<SecurityOptions>(options =>
options.PortalVisibility.UnfilteredActivities.Add("recurringappointmentmaster"));
A new appointment, or a change to one, becomes a series in the same transaction as the rest of its save: the save sends an AddRecurrenceRequest beside the appointment's own, so the appointment is never saved without its series, nor the series without it. Dataverse creates the series from the appointment, with its attendees, and removes the appointment. A page building its own form can send the request the same way.
A series that has begun changes from its next occurrence on, as in Outlook: Dataverse keeps the occurrences that have ended as a series of their own, sharing the series' groupid, so they stay as they took place. The count a series ends after is of the whole series — the portal reads and saves it with the occurrences already kept — and a series with none left to come can no longer be changed. Switching All day fits the times to it: on, the whole days they fall on; off, nine o'clock on the first of them for half an hour. Dataverse keeps an all-day appointment as a day; the portal shows and saves it on the user's own day, whatever their time zone.
Pass Appointments instead, and the calendar shows those rather than reading Dataverse. With Editable on, appointments can be moved, resized and created by selecting a range: SaveChange (React: onAppointmentChange) saves a move, and returning false puts the appointment back; OnRangeSelect receives a selected range, and OnAppointmentClick an opened appointment.
<Calendar appointments={appointments}
editable
onAppointmentChange={saveMove}
onAppointmentClick={openAppointment} /><Calendar Appointments="appointments"
Editable="true"
SaveChange="SaveMoveAsync"
OnAppointmentClick="OpenAppointment" />Resting the pointer on an appointment shows its subject, when and where it is, and clicking one opens a dialog of its details, read from Dataverse as a record page reads a record — through the same table access and column security — and shown read-only. Handle OnAppointmentClick (React: onAppointmentClick) to do something else on a click, such as opening a page of your own. The calendar's rows, its tooltips and the details dialog can each be drawn your own way: see Custom Rendering.
Put your own controls in the toolbar with ToolbarItems (React: the calendar's children), ahead of the search box, as a grid's toolbar buttons are. A CalendarButton is handed the calendar when clicked: the period it shows, the record it is about, and CreateAppointmentAsync and RefreshAsync (React: createAppointment and refresh). NewAppointmentCalendarButton starts a new appointment as selecting a time does — from the next half hour when the period shown takes in today, and otherwise from 8:00 on its first day — and is shown only where that starts something: an Editable calendar whose host handles OnRangeSelect, or that loads its own appointments for a user who may create them.
<Calendar regarding={account} editable>
<NewAppointmentCalendarButton />
{/* Your own button, handed the calendar when clicked */}
<CalendarButton
label="Export"
icon={<ArrowDownloadRegular />}
onClick={(calendar) => exportAsync(calendar.start, calendar.end)}
/>
</Calendar><Calendar Regarding="@account" Editable="true">
<ToolbarItems>
<NewAppointmentCalendarButton />
<!-- Your own button, handed the calendar when clicked -->
<CalendarButton Label="Export"
Icon="@(new Icons.Regular.Size20.ArrowDownload())"
OnClick="@((CalendarContext calendar) => ExportAsync(calendar.Start, calendar.End))" />
</ToolbarItems>
</Calendar>The calendar lays its appointments out by the hours of a day, a work week or a week, as a month of days, or as an agenda list, and its toolbar has a picker to switch between them. DefaultLayout (React: defaultLayout) is the one it opens in, a week unless you choose another. Layouts (React: layouts) keeps it to the ones you name, listed in your order: name one, and the picker goes and the calendar stays in it. Each layout has a page of its own: Day, Work Week, Week, Month and Agenda.
<Calendar defaultLayout="month" layouts={['month', 'agenda']} /><Calendar DefaultLayout="CalendarLayout.Month"
Layouts="@([CalendarLayout.Month, CalendarLayout.Agenda])" />Offer views in ViewIds — Dataverse views, each of its own table — MultiTableViewIds, views of several tables registered with AddMultiTableView or kept in Dataverse, and CustomViewDefinitions, views defined in code, and the toolbar has a picker to choose what the calendar shows. DefaultViewId is the one it opens on. The search box finds each table's search columns as a grid's search does. Turn it off with AllowSearch.
With AllowFiltering, the toolbar's Filter menu offers Edit filters, which opens the filters of the tables shown, a tab for each in order of its name, each the dialog a grid's Filter button opens: conditions on the table's columns, nested in groups, and related tables to filter by, over the columns and tables the user may read. A tab's columns are read when it is first opened. The filters take the place of the view's own until the user picks another view. It is off by default, because the user can take away every filter the view has.
With AllowSavedViews (React: allowSavedViews), a signed-in user can save what the calendar shows — the view, their filter, the tables shown and the layout — as a view of their own, choose their default view and hide the ones they don't use, from the toolbar's Filter menu. A view of appointments alone is offered on the grids of appointments too, and a view of several tables on timelines. How views are saved, changed and shared is on the Saved Views page.
<Calendar viewIds={[allAppointmentsViewId, scheduledViewId]}
defaultViewId={allAppointmentsViewId}
customViewDefinitions={teamsMeetings}
allowFiltering
allowSavedViews /><Calendar ViewIds="@([allAppointmentsViewId, scheduledViewId])"
DefaultViewId="allAppointmentsViewId"
CustomViewDefinitions="teamsMeetings"
AllowFiltering="true"
AllowSavedViews="true" />Schmeler and Sons' meetings, tasks and phone calls, through two views defined in code: Meetings and tasks, and Still to do — the tasks and calls not yet done, each table's query its own. The Filter menu switches each table on and off, Edit filters has a tab for each, and a signed-in visitor can save what the calendar shows as a view of their own. A task or call opens in a side panel on the right (FormLocation).
Name a query parameter in PersistedStateQueryParameter, and the calendar keeps its layout, the period it shows and the appointment view picked there, so a reload or a shared link opens it where it was left. What it would open on anyway is left out — the date while the period shown includes today, so a link to the current week keeps following today. The search is kept too when you turn on IncludeSearchInPersistedState; it is left out by default, since what users search for can be sensitive. Each change replaces the browser's history entry, so Back leaves the page rather than stepping back through every week. Both stacks write the same value, so a link made on one opens the same calendar on the other.
<Calendar defaultLayout="month" persistedStateQueryParameter="calendarState" /><Calendar DefaultLayout="CalendarLayout.Month" PersistedStateQueryParameter="calendarState" />A view defined in code keeps the calendar to the meetings held in Teams, opening on the week's agenda, with a set height.
The calendar is an add-on: PowerPortalsPro.Web.Blazor.Calendar on Blazor and @powerportalspro/react-calendar on React. Add the package beside the framework's own to use it.
npm install @powerportalspro/react-calendardotnet add package PowerPortalsPro.Web.Blazor.CalendarName | Type | Default | Description |
|---|---|---|---|
AllowFiltering | bool | False | When Calendar.Appointments the host gives the calendar, nor for a view of several tables kept in Dataverse. Defaults to |
AllowSavedViews | bool | False | Lets signed-in users save the calendar as they've arranged it — the view it shows with their filter, and its layout — as views of their own, offered in the view picker beside the page's, and choose their default view and hide the views they don't use. A view saved on a calendar or a grid of appointments with no Calendar.SavedViewsKey is offered on both, each keeping its own layout of it. Off by default. Needs the Power Portals Pro Core solution's saved-view tables; an internal user also needs the |
AllowSearch | bool | True | Whether the toolbar has a search box, when the calendar loads its own appointments. A search finds each table's search columns as a grid's search does. |
AppointmentDetails | RenderFragment? | The fields the details dialog shows, in place of the calendar's own: column edits and the like, bound to the appointment's record as on a record page. The calendar's own show when it is, where, what it is regarding, its status and its description, read-only. | |
Appointments | IReadOnlyList<CalendarAppointment>? | The appointments to show, when the host has them. Without them, the calendar loads the rows of each range it shows from Dataverse — appointments, tasks and the other tables the server registered — as the view picked, Calendar.Regarding and the user's access allow. | |
ChildContent | RenderFragment? | The page's Components.MultiTableTemplates — how a table's rows are drawn on the calendar, in place of all the calendar draws of them: their time, subject, place and repeat icon — and Components.MultiTableForms: the forms a row of another table than appointments opens in, by table, in place of the default one. A template is for the rows the calendar loads, not for Calendar.Appointments the host gives it. It is drawn as each range is read, so a change to what it shows reaches the calendar with the next range or refresh; and what it draws is copied into the appointment, so it shows, but doesn't respond to the user, as a tooltip doesn't. | |
CssClass | string? | Extra classes for the calendar's root. | |
CustomViewDefinitions | List<MultiTableViewDefinition>? | Views defined in code, of one table or several, offered beside Calendar.ViewIds: each table with which of its rows show. | |
DefaultLayout | CalendarLayout | Week | The layout it opens in: the hours of a day, a work week or a week, a month of days, or an agenda list. Defaults to CalendarLayout.Week; the first of Calendar.Layouts when that leaves it out. |
DefaultTables | IEnumerable<string>? | The tables shown at first when the calendar shows every table — no view of its own: the others the user may read are offered in the table menu, switched off. Defaults to appointments and tasks. With none of them to show, every table is. | |
DefaultViewId | Guid? | The view the calendar opens on, one of those offered. Without it, the first of Calendar.ViewIds, then of Calendar.MultiTableViewIds, then of Calendar.CustomViewDefinitions. | |
Editable | bool | False | Whether appointments can be moved, resized and created by selecting a range. A loaded row can be moved when the user may change it and its start, and it isn't closed — completed, canceled or inactive; resized when they may change its end too. The calendar saves the move, unless Calendar.SaveChange does. |
FormLocation | DialogLocation | Center | Where an appointment's details and another table's form open: a dialog in the center, or a side panel on the left or the right. Defaults to DialogLocation.Center. |
Height | string | 600px | Height of the whole calendar, toolbar included. |
IncludeSearchInPersistedState | bool | False | Whether the search is kept in the URL with the rest of the calendar's state. Off by default: what users search for can be sensitive, and stays in the browser's history and in referrers. Has no effect without Calendar.PersistedStateQueryParameter. |
Layouts | IReadOnlyList<CalendarLayout>? | The layouts the user can switch between, in the order the picker lists them. With one, the picker is hidden and the calendar stays in it: a link naming another is ignored, and a day's number no longer opens that day unless CalendarLayout.Day is offered. Every layout when null or empty. | |
MatchParties | bool? | Whether activities the Calendar.Regarding record takes part in — as a sender, recipient or attendee — show beside those about it. Unset, each table's setting, else the server's. | |
MultiTableViewIds | IEnumerable<Guid>? | Views of several tables the user can pick from: ones the server registered ( | |
PersistedStateQueryParameter | string? | The name of a URL query parameter to keep the calendar's state in — its layout, the period it shows, the appointment view picked and (with Calendar.IncludeSearchInPersistedState) the search — so a reload or a link opens it where it was left. Each change replaces the browser's history entry rather than adding one. What the calendar would open on anyway is left out: the layout while it is the one it opens in, the appointment view while it is the default one, and the date while the period shown takes in today, so a link to the current week keeps following today. The URL is read once, when the calendar opens; a value it can't read is ignored. | |
Regarding | TableRecordReference? | Only the rows about this record, when the calendar loads its own: through each table's regarding column, and the activities it takes part in (see Calendar.MatchParties). | |
RegardingTables | IReadOnlyList<string>? | The tables an appointment's Regarding can name a record of, in the order its form's table picker lists them. Tables Regarding can't point to are ignored. Every one it can when null; either way, only the tables whose records the user may read and point a lookup to are offered. | |
SaveChange | Func<CalendarAppointmentChange, Task<bool>>? | Saves a move or resize in place of the calendar; returning false puts the appointment back, as when the save is refused. Without it, a given appointment's change stands, and the calendar saves a loaded row's: its start, its end, and whether it takes whole days. | |
SavedViewsKey | string? | Names this calendar among the controls of appointments, so the views saved on it and the user's default and hidden views are its own: it offers the views saved on it as well as the views saved on controls with no key, and its default and hidden views are set for it alone. Without one, the calendar shares its views with every grid and calendar of appointments that has none, and its default and hidden views with every calendar that has none. Letters, digits, | |
Series | MultiTableSeries? | How recurring appointments show. Unset, each table's setting: each occurrence on its own. | |
ShowRecordLinks | bool | True | Whether an appointment's details link to its page, and its Regarding to the record's page, registered with true; a table with no page shows no link. |
SignInUrl | string | Account/Login | Where a user who isn't signed in is sent to sign in, from the saved views' menu, which asks them to first; they come back to this page ( |
Tables | IEnumerable<string>? | The tables the calendar may show, by logical name: of those the server registered ( | |
ToolbarItems | RenderFragment? | Controls for the toolbar: Components.CalendarButtons, such as Components.NewAppointmentCalendarButton, or any other markup. They appear in the order they are declared, ahead of the search box, as a grid's | |
TooltipTemplate | RenderFragment<CalendarAppointment>? | What an appointment's tooltip shows, in place of its subject, when, where and whether it repeats: given the appointment, whose CalendarAppointment.Record holds every column of the view for one the calendar loaded. A tooltip isn't interactive, so it shows what the template draws, not controls to use. | |
ViewIds | IEnumerable<Guid>? | Dataverse views the user can pick from, by id: each shows its own table's rows that it picks. With more than one view on offer — these, Calendar.MultiTableViewIds and Calendar.CustomViewDefinitions — the toolbar has a picker. With none, the calendar shows every table (see Calendar.DefaultTables). | |
ViewSort | ViewSort | NameAscending | The order of the views in the picker. |
AllowFilteringCalendar.Appointments the host gives the calendar, nor for a view of several tables kept in Dataverse. Defaults to AllowSavedViewsCalendar.SavedViewsKey is offered on both, each keeping its own layout of it. Off by default. Needs the Power Portals Pro Core solution's saved-view tables; an internal user also needs the AllowSearchAppointmentDetailsAppointmentsCalendar.Regarding and the user's access allow.ChildContentComponents.MultiTableTemplates — how a table's rows are drawn on the calendar, in place of all the calendar draws of them: their time, subject, place and repeat icon — and Components.MultiTableForms: the forms a row of another table than appointments opens in, by table, in place of the default one. A template is for the rows the calendar loads, not for Calendar.Appointments the host gives it. It is drawn as each range is read, so a change to what it shows reaches the calendar with the next range or refresh; and what it draws is copied into the appointment, so it shows, but doesn't respond to the user, as a tooltip doesn't.CssClassCustomViewDefinitionsCalendar.ViewIds: each table with which of its rows show.DefaultLayoutCalendarLayout.Week; the first of Calendar.Layouts when that leaves it out.DefaultTablesDefaultViewIdCalendar.ViewIds, then of Calendar.MultiTableViewIds, then of Calendar.CustomViewDefinitions.EditableCalendar.SaveChange does.FormLocationDialogLocation.Center.HeightIncludeSearchInPersistedStateCalendar.PersistedStateQueryParameter.LayoutsCalendarLayout.Day is offered. Every layout when null or empty.MatchPartiesCalendar.Regarding record takes part in — as a sender, recipient or attendee — show beside those about it. Unset, each table's setting, else the server's.MultiTableViewIdsPersistedStateQueryParameterCalendar.IncludeSearchInPersistedState) the search — so a reload or a link opens it where it was left. Each change replaces the browser's history entry rather than adding one. What the calendar would open on anyway is left out: the layout while it is the one it opens in, the appointment view while it is the default one, and the date while the period shown takes in today, so a link to the current week keeps following today. The URL is read once, when the calendar opens; a value it can't read is ignored.RegardingCalendar.MatchParties).RegardingTablesSaveChangeSavedViewsKeySeriesShowRecordLinkstrue; a table with no page shows no link.SignInUrlTablesToolbarItemsComponents.CalendarButtons, such as Components.NewAppointmentCalendarButton, or any other markup. They appear in the order they are declared, ahead of the search box, as a grid's TooltipTemplateCalendarAppointment.Record holds every column of the view for one the calendar loaded. A tooltip isn't interactive, so it shows what the template draws, not controls to use.ViewIdsCalendar.MultiTableViewIds and Calendar.CustomViewDefinitions — the toolbar has a picker. With none, the calendar shows every table (see Calendar.DefaultTables).ViewSortName | Type | Description |
|---|---|---|
OnAppointmentClick | EventCallback<string> | An appointment was clicked or activated from the keyboard. Without it, an appointment the calendar loaded opens in a dialog of its details, and a row of another table in its form (see Calendar.ChildContent). |
OnRangeSelect | EventCallback<CalendarRangeSelection> | A range was selected to create an appointment in. |
OnAppointmentClickCalendar.ChildContent).OnRangeSelectName | Parameters | Type | Description |
|---|---|---|---|
CreateAppointmentAsync | DateTimeOffset? start DateTimeOffset? end bool allDay | Task | Starts a new appointment, as selecting a time does in an Calendar.Editable calendar: the host's to handle when it handles Calendar.OnRangeSelect; otherwise, in a calendar that loads its own appointments, a form to create one, when the user may. Without a time, the appointment starts at the next half hour when the period shown takes in today, and otherwise at 8:00 on its first day, and lasts half an hour. Does nothing unless the calendar is Calendar.Editable. |
LoadAppointmentsAsync | DateTimeOffset start DateTimeOffset end | Task<IReadOnlyList<CalendarAppointment>> | The rows of a range, for the script's event source when the calendar loads its own. A table that couldn't be read is named above the calendar, and a failure of the whole read is shown there and leaves the range empty. |
OnAppointmentChangeAsync | CalendarAppointmentChange change | Task<bool> | An appointment was moved or resized: saved by the host's Calendar.SaveChange, else — for a row the calendar loaded — by the calendar. False puts it back. |
OnAppointmentClickAsync | string id string subject bool recurring string table | Task | An appointment was clicked: a row of another table than appointments names its table. |
OnRangeChanged | RangeChanged range | void | The visible range changed; the toolbar follows it, and the URL when it keeps the state. |
OnRangeSelectAsync | DateTimeOffset start DateTimeOffset end bool allDay | Task | A range was selected, in an editable calendar: the host's to handle when it says so ( Calendar.OnRangeSelect); otherwise a calendar that loads its own appointments opens a new one to create over it — when the user may create appointments, and nothing when they may not. |
RefreshAsync | Task | Reads the range shown again, for appointments changed since it was read. | |
RenderTooltip | string id | int | The pointer or the focus has rested on an appointment: its Calendar.TooltipTemplate is drawn, for the script to copy into the tooltip it shows. Returns the number of that render, which the script waits to see on the drawing; 0 when there is nothing to draw, and the tooltip keeps the calendar's own. |
CreateAppointmentAsyncCalendar.Editable calendar: the host's to handle when it handles Calendar.OnRangeSelect; otherwise, in a calendar that loads its own appointments, a form to create one, when the user may. Without a time, the appointment starts at the next half hour when the period shown takes in today, and otherwise at 8:00 on its first day, and lasts half an hour. Does nothing unless the calendar is Calendar.Editable.LoadAppointmentsAsyncOnAppointmentChangeAsyncCalendar.SaveChange, else — for a row the calendar loaded — by the calendar. False puts it back.OnAppointmentClickAsyncOnRangeChangedOnRangeSelectAsyncCalendar.OnRangeSelect); otherwise a calendar that loads its own appointments opens a new one to create over it — when the user may create appointments, and nothing when they may not.RefreshAsyncRenderTooltipCalendar.TooltipTemplate is drawn, for the script to copy into the tooltip it shows. Returns the number of that render, which the script waits to see on the drawing; 0 when there is nothing to draw, and the tooltip keeps the calendar's own.