windowsforms-devexpress-dot-xtrascheduler-dot-microsoft365calendar-dot-dxoutlook365sync.md
A component that allows you to synchronize user appointments in WinForms Scheduler/WPF Scheduler with Outlook 365 Calendars (bi-directionally).
Namespace : DevExpress.XtraScheduler.Microsoft365Calendar
Assembly : DevExpress.XtraScheduler.v25.2.Microsoft365Calendar.dll
NuGet Package : DevExpress.Scheduler.Core.Desktop.Microsoft365Calendar
public class DXOutlook365Sync :
Component
Public Class DXOutlook365Sync
Inherits Component
The following GitHub examples demonstrate how to synchronize user appointments with Microsoft 365 calendars (import, export, merge):
Install the following dependency libraries (NuGet packages):
Then add the DXOutlook365Sync component to the project.
WinForms Project
Use the Scheduler smart tag menu to add DXOutlook365Sync to the project. The Sync with Microsoft 365 Calendar menu item is not displayed if the required dependency libraries are not installed. DXOutlook365Sync automatically fills its Storage after it is created.
Note
The “Sync with Microsoft 365 Calendar” smart tag menu item is not available for .NET 6+ applications.
WPF Project
Add the DXOutlook365Sync component to the project as follows. Note that you need to use a SchedulerControl.CreateStorageAdapter method to fill the component’s Storage.
using DevExpress.Xpf.Scheduling;
using DevExpress.XtraScheduler.Microsoft365Calendar;
// ...
public partial class MainWindow : Window {
DXOutlook365Sync dXOutlook365Sync;
public MainWindow() {
InitializeComponent();
dXOutlook365Sync = new DXOutlook365Sync(uiScheduler.CreateStorageAdapter());
//...
}
// ...
}
Imports DevExpress.Xpf.Scheduling
Imports DevExpress.XtraScheduler.Microsoft365Calendar
' ...
Public Partial Class MainWindow
Inherits Window
Private dXOutlook365Sync As DXOutlook365Sync
Public Sub New()
InitializeComponent()
dXOutlook365Sync = New DXOutlook365Sync(uiScheduler.CreateStorageAdapter())
' ...
End Sub
' ...
End Class
Important
You must initialize the DXOutlook365Sync component before using its API. Otherwise, the DXOutlook365Sync component throws an exception.
Call the InitAsync method to initialize the DXOutlook365Sync component. The method opens a Sign in to your account window that requires you to log in to Microsoft 365.
The DXOutlook365Sync component requires that you register your application in Azure as demonstrated in the following topic: Register an application with the Microsoft identity platform. After the registration, you can use one of the techniques below to pass registration data to the DXOutlook365Sync component:
Azure.Core.TokenCredential and pass this instance to the DXOutlook365Sync(ISchedulerStorageBase, IOutlookEngine) constructor or InitAsync(IOutlookEngine) method.The InitAsync method returns an InitStatus enumeration value that indicates whether the initialization succeeded, failed, or has already been initialized. The InitAsync method does nothing if the DXOutlook365Sync component has already been initialized.
The following example demonstrates how to create and initialize the DXOutlook365Sync component:
using DevExpress.XtraScheduler.Microsoft365Calendar;
DXOutlook365Sync dxOutlook365Sync1;
public Form1() {
InitializeComponent();
// Install the required dependency libraries before using the DXOutlook365Sync component.
dxOutlook365Sync1 = new DXOutlook365Sync(schedulerDataStorage1);
// Initializes the 'dxOutlook365Sync1' component.
string tenantId = "..."; // Enter your tenant (directory) ID.
string clientId = "..."; // Enter your client (application) ID.
InitStatus status = await outlook365sync.InitAsync(tenantId, clientId);
// Returns false and displays a message box if the initialization of 'dxOutlook365Sync1' failed.
if(status == InitStatus.Error)
XtraMessageBox.Show("Initialization of DXOutlook365Sync failed.", "Error", MessageBoxButtons.OK);
}
Imports DevExpress.XtraScheduler.Microsoft365Calendar
Private dxOutlook365Sync1 As DXOutlook365Sync
Public Sub New()
InitializeComponent()
' Install the required dependency libraries before using the DXOutlook365Sync component.
dxOutlook365Sync1 = New DXOutlook365Sync(schedulerDataStorage1)
' Initializes the 'dxOutlook365Sync1' component.
Dim tenantId As String = "..." ' Enter your tenant (directory) ID.
Dim clientId As String = "..." ' Enter your client (application) ID.
Dim status As InitStatus = Await outlook365sync.InitAsync(tenantId, clientId)
' Returns false and displays a message box if the initialization of 'dxOutlook365Sync1' failed.
If status Is InitStatus.Error Then
XtraMessageBox.Show("Initialization of DXOutlook365Sync failed.", "Error", MessageBoxButtons.OK)
End If
End Sub
The DXOutlook365Sync component raises the InitComplete event once its initialization is finished (see the following example). The e.InitStatus parameter specifies whether the initialization succeeded or failed. Use the e.Exception property to get the description of the exception.
private void DxOutlook365Sync1_InitComplete(object sender, InitCompleteEventArgs e) {
if(e.InitStatus == InitStatus.Error)
XtraMessageBox.Show(String.Format("Initialization of DXOutlook365Sync failed. {0}", e.Exception.Message), "Error", MessageBoxButtons.OK);
}
Private Sub DxOutlook365Sync1_InitComplete(ByVal sender As Object, ByVal e As InitCompleteEventArgs)
If e.InitStatus = InitStatus.Error Then
XtraMessageBox.Show(String.Format("Initialization of DXOutlook365Sync failed. {0}", e.Exception.Message), "Error", MessageBoxButtons.OK)
End If
End Sub
The DXOutlook365Sync component automatically populates its Calendars collection if the initialization was successful. This collection contains OutlookCalendarItem objects that correspond to Office365 calendars.
A calendar (OutlookCalendarItem) has the EnableSynchronization option that specifies whether to synchronize its events with user appointments in the Scheduler control.
The DXOutlook365Sync component includes the following methods to enable or disable synchronization for calendars:
Use the SynchronizeCalendarsAsync() method to force the DXOutlook365Sync component to synchronize (reload) its Calendars collection with Microsoft 365 calendars.
The DXOutlook365Sync component raises the CalendarSynchronizeComplete event when the calendar synchronization is complete. Use the e.Exception event parameter to get a description of the error if the operation failed.
The ImportOutlookToSchedulerAsync(Boolean) method imports events from Microsoft 365 calendars with synchronization enabled.
This example demonstrates how to import events from all Outlook 365 calendars to the WinForms Scheduler control.
using DevExpress.XtraScheduler.Microsoft365Calendar;
DXOutlook365Sync dxOutlook365Sync1;
public Form1() {
InitializeComponent();
dxOutlook365Sync1 = new DXOutlook365Sync(schedulerDataStorage1);
dxOutlook365Sync1.InitComplete += DxOutlook365Sync1_InitComplete;
}
private async Task<bool> InitOutlook365Sync(DXOutlook365Sync outlook365sync) {
// Initializes the 'dxOutlook365Sync1' component.
string tenantId = "..."; // Enter your tenant (directory) ID.
string clientId = "..."; // Enter your client (application) ID.
InitStatus status = await outlook365sync.InitAsync(tenantId, clientId);
// Returns false and displays a message box if the initialization of 'dxOutlook365Sync1' failed.
if(status == InitStatus.Error) {
XtraMessageBox.Show("Initialization of DXOutlook365Sync failed.", "Error", MessageBoxButtons.OK);
return false;
}
return true;
}
private async void importEventsButton_Click(object sender, EventArgs e) {
// Checks whether the initialization of 'dxOutlook365Sync1' failed.
if(!await InitOutlook365Sync(dxOutlook365Sync1)) return;
// Displays the wait form.
splashScreenManager1.ShowWaitForm();
// Imports Outlook 365 events to the Scheduler control.
await dxOutlook365Sync1.ImportOutlookToSchedulerAsync(false);
// Hides the wait form.
splashScreenManager1.CloseWaitForm();
}
Imports DevExpress.XtraScheduler.Microsoft365Calendar
Private dxOutlook365Sync1 As DXOutlook365Sync
Public Sub New()
InitializeComponent()
dxOutlook365Sync1 = New DXOutlook365Sync(schedulerDataStorage1)
dxOutlook365Sync1.InitComplete += DxOutlook365Sync1_InitComplete
End Sub
Private Async Function InitOutlook365Sync(ByVal outlook365sync As DXOutlook365Sync) As Task(Of Boolean)
' Initializes the 'dxOutlook365Sync1' component.
Dim tenantId As String = "..." ' Enter your tenant (directory) ID.
Dim clientId As String = "..." ' Enter your client (application) ID.
Dim status As InitStatus = Await outlook365sync.InitAsync(tenantId, clientId)
' Returns false and displays a message box if the initialization of 'dxOutlook365Sync1' failed.
If status Is InitStatus.Error Then
XtraMessageBox.Show("Initialization of DXOutlook365Sync failed.", "Error", MessageBoxButtons.OK)
Return False
End If
Return True
End Function
Private Async Sub importEventsButton_Click(ByVal sender As Object, ByVal e As EventArgs)
' Checks whether the initialization of 'dxOutlook365Sync1' failed.
If (Not Await) InitOutlook365Sync(dxOutlook365Sync1) Then
Return
End If
' Displays the wait form.
splashScreenManager1.ShowWaitForm()
' Imports Outlook 365 events to the Scheduler control.
Await dxOutlook365Sync1.ImportOutlookToSchedulerAsync(False)
' Hides the wait form.
splashScreenManager1.CloseWaitForm()
End Sub
The ExportSchedulerToOutlookAsync(Boolean) method exports the appointments from the Scheduler control to the Office365 calendars with synchronization enabled. Appointments that do not have corresponding events in Microsoft 365 are exported to the default calendar in Office365.
using DevExpress.XtraScheduler.Microsoft365Calendar;
DXOutlook365Sync dxOutlook365Sync1;
public Form1() {
InitializeComponent();
dxOutlook365Sync1 = new DXOutlook365Sync(schedulerDataStorage1);
dxOutlook365Sync1.InitComplete += DxOutlook365Sync1_InitComplete;
}
private async Task<bool> InitOutlook365Sync(DXOutlook365Sync outlook365sync) {
// Initializes the 'dxOutlook365Sync1' component.
string tenantId = "..."; // Enter your tenant (directory) ID.
string clientId = "..."; // Enter your client (application) ID.
InitStatus status = await outlook365sync.InitAsync(tenantId, clientId);
// Returns false if the initialization of 'dxOutlook365Sync1' failed.
return status != InitStatus.Error;
}
private async void exportAppointmentsButton_Click(object sender, EventArgs e) {
// Checks whether the initialization of 'dxOutlook365Sync1' failed.
if(!await InitOutlook365Sync(dxOutlook365Sync1)) return;
splashScreenManager1.ShowWaitForm();
// Exports the Scheduler control's appointments to Outlook365.
await dxOutlook365Sync1.ExportSchedulerToOutlookAsync(false);
splashScreenManager1.CloseWaitForm();
}
private void DxOutlook365Sync1_InitComplete(object sender, InitCompleteEventArgs e) {
if(e.InitStatus == InitStatus.Error)
XtraMessageBox.Show(String.Format("Initialization of DXOutlook365Sync failed. {0}", e.Exception.Message), "Error", MessageBoxButtons.OK);
}
Imports DevExpress.XtraScheduler.Microsoft365Calendar
Private dxOutlook365Sync1 As DXOutlook365Sync
Public Sub New()
InitializeComponent()
dxOutlook365Sync1 = New DXOutlook365Sync(schedulerDataStorage1)
AddHandler dxOutlook365Sync1.InitComplete, AddressOf DxOutlook365Sync1_InitComplete
End Sub
Private Async Function InitOutlook365Sync(ByVal outlook365sync As DXOutlook365Sync) As Task(Of Boolean)
' Initializes the 'dxOutlook365Sync1' component.
Dim tenantId As String = "..." ' Enter your tenant (directory) ID.
Dim clientId As String = "..." ' Enter your client (application) ID.
Dim status As InitStatus = Await outlook365sync.InitAsync(tenantId, clientId)
' Returns false if the initialization of 'dxOutlook365Sync1' failed.
Return status IsNot InitStatus.Error
End Function
Private Async Sub exportAppointmentsButton_Click(ByVal sender As Object, ByVal e As EventArgs)
' Checks whether the initialization of 'dxOutlook365Sync1' failed.
If (Not Await) InitOutlook365Sync(dxOutlook365Sync1) Then
Return
End If
splashScreenManager1.ShowWaitForm()
' Exports the Scheduler control's appointments to Outlook365.
Await dxOutlook365Sync1.ExportSchedulerToOutlookAsync(False)
splashScreenManager1.CloseWaitForm()
End Sub
Private Sub DxOutlook365Sync1_InitComplete(ByVal sender As Object, ByVal e As InitCompleteEventArgs)
If e.InitStatus = InitStatus.Error Then
XtraMessageBox.Show(String.Format("Initialization of DXOutlook365Sync failed. {0}", e.Exception.Message), "Error", MessageBoxButtons.OK)
End If
End Sub
Use the following methods to merge the Scheduler control with Microsoft 365 calendars:
Handle the MergeSingleItem event to specify a merge action based on a condition. The MergeSingleItem event occurs for each appointment/event pair during an export, an import, or a merge operation and allows you to redefine a merge action based on a condition.
Use the e.ActionType property to specify the merge action.
The following example demonstrates how to import Outlook 365 events that have not started.
using DevExpress.XtraScheduler.Microsoft365Calendar;
dxOutlook365Sync1.MergeSingleItem += DxOutlook365Sync1_MergeSingleItem;
private void DxOutlook365Sync1_MergeSingleItem(object sender, Outlook365CalendarMergeEventArgs e) {
if(e.OutlookEvent != null && e.OutlookEvent.Start.ToDateTime() < DateTime.Now)
e.ActionType = MergeActionType.DoNothing;
}
Imports DevExpress.XtraScheduler.Microsoft365Calendar
Private dxOutlook365Sync1.MergeSingleItem += AddressOf DxOutlook365Sync1_MergeSingleItem
Private Sub DxOutlook365Sync1_MergeSingleItem(ByVal sender As Object, ByVal e As Outlook365CalendarMergeEventArgs)
If e.OutlookEvent IsNot Nothing AndAlso e.OutlookEvent.Start.ToDateTime() < Date.Now Then
e.ActionType = MergeActionType.DoNothing
End If
End Sub
If you make conflicting edits to a Scheduler appointment and an Microsoft 365 event, the DXOutlook365Sync component is unable to identify which of these objects holds valid data. The component raises the MergeConflictResolve event to allow you to resolve a merge conflict.
Handle the MergeConflictResolve event and set the e.ActionType parameter to MergeActionType.InsertOrUpdateEvent to copy a Scheduler appointment to a Microsoft 365 calendar. Otherwise, set the parameter to MergeActionType.InsertOrUpdateAppointment.
using DevExpress.XtraScheduler.Microsoft365Calendar;
dxOutlook365Sync1.MergeConflictResolve += DxOutlook365Sync1_MergeConflictResolve;
private void DxOutlook365Sync1_MergeConflictResolve(object sender, Outlook365CalendarMergeEventArgs e) {
DateTime appointmentLastModified = e.SchedulerAppointment.GetSchedulerChangedUTC().Value;
DateTime eventLastModified = e.OutlookEvent.LastModifiedDateTime.Value.UtcDateTime;
e.ActionType = appointmentLastModified > eventLastModified ? MergeActionType.InsertOrUpdateEvent : MergeActionType.InsertOrUpdateAppointment;
}
Imports DevExpress.XtraScheduler.Microsoft365Calendar
Private dxOutlook365Sync1.MergeConflictResolve += AddressOf DxOutlook365Sync1_MergeConflictResolve
Private Sub DxOutlook365Sync1_MergeConflictResolve(ByVal sender As Object, ByVal e As Outlook365CalendarMergeEventArgs)
Dim appointmentLastModified As Date = e.SchedulerAppointment.GetSchedulerChangedUTC().Value
Dim eventLastModified As Date = e.OutlookEvent.LastModifiedDateTime.Value.UtcDateTime
e.ActionType = If(appointmentLastModified > eventLastModified, MergeActionType.InsertOrUpdateEvent, MergeActionType.InsertOrUpdateAppointment)
End Sub
The DXOutlook365Sync component raises MergeComplete after a merge operation has completed.
Handle the following events to customize a user appointment or event before synchronization:
The following example checks the description of appointments when they are exported (or merged) to a Microsoft 365 calendar. It adds a general description to the event before it is added to the calendar if the corresponding appointment does not have a description.
using DevExpress.XtraScheduler.Microsoft365Calendar;
private void DxOutlook365Sync1_CustomizeAppointmentToEvent(object sender, ConvertEventArgs e) {
if(e.Appointment != null && e.Appointment.Description == string.Empty)
e.Event.Body.Content = string.Format("Please describe '{0}' event.", e.Appointment.Subject);
}
Imports DevExpress.XtraScheduler.Microsoft365Calendar
Private Sub DxOutlook365Sync1_CustomizeAppointmentToEvent(ByVal sender As Object, ByVal e As ConvertEventArgs)
If e.Appointment IsNot Nothing AndAlso e.Appointment.Description = String.Empty Then
e.Event.Body.Content = String.Format("Please describe '{0}' event.", e.Appointment.Subject)
End If
End Sub
The DXOutlook365Sync component requires five special fields in a data source to store identifiers of Microsoft 365 events and calendars, and the last modified date of user appointments and Microsoft 365 events.
Add the corresponding fields to a data source and define custom mappings to these fields. Appointment properties must have the following names and type:
| Custom Field Name | Property Type |
|---|---|
outlook365_calendar_id | System.String |
outlook365_event_id | System.String |
outlook365_event_ICalUId | System.String |
outlook365_lastChangedUTC | System.DateTime |
scheduler_lastChangedUTC | System.DateTime |
The following example demonstrates how to create a DataTable object with special fields and define custom mappings:
DataTable source = new DataTable();
source.Columns.AddRange(new DataColumn[] {
new DataColumn("Subject", typeof(string)),
new DataColumn("Description", typeof(string)),
new DataColumn("Start", typeof(DateTime)),
new DataColumn("End", typeof(DateTime)),
// Special data fields.
new DataColumn("Outlook365CalendarId", typeof(string)),
new DataColumn("Outlook365EventId", typeof(string)),
new DataColumn("Outlook365EventUniqId", typeof(string)),
new DataColumn("Outlook365LastChangedUTC", typeof(DateTime)),
new DataColumn("SchedulerLastChangedUTC", typeof(DateTime))
});
// Binds the Scheduler Data Storage component to data and defines common mappings.
schedulerDataStorage1.Appointments.DataSource = source;
schedulerDataStorage1.Appointments.Mappings.Subject = "Subject";
schedulerDataStorage1.Appointments.Mappings.Description = "Description";
schedulerDataStorage1.Appointments.Mappings.Start = "Start";
schedulerDataStorage1.Appointments.Mappings.End = "End";
// Defines custom mappings.
schedulerDataStorage1.Appointments.CustomFieldMappings.Add(new AppointmentCustomFieldMapping("outlook365_calendar_id", "Outlook365CalendarId"));
schedulerDataStorage1.Appointments.CustomFieldMappings.Add(new AppointmentCustomFieldMapping("outlook365_event_id", "Outlook365EventId"));
schedulerDataStorage1.Appointments.CustomFieldMappings.Add(new AppointmentCustomFieldMapping("outlook365_event_ICalUId", "Outlook365EventUniqId"));
schedulerDataStorage1.Appointments.CustomFieldMappings.Add(new AppointmentCustomFieldMapping("outlook365_lastChangedUTC", "Outlook365LastChangedUTC"));
schedulerDataStorage1.Appointments.CustomFieldMappings.Add(new AppointmentCustomFieldMapping("scheduler_lastChangedUTC", "SchedulerLastChangedUTC"));
Dim source As New DataTable()
source.Columns.AddRange(New DataColumn() {
New DataColumn("Subject", GetType(String)),
New DataColumn("Description", GetType(String)),
New DataColumn("Start", GetType(Date)),
New DataColumn("End", GetType(Date)),
New DataColumn("Outlook365CalendarId", GetType(String)),
New DataColumn("Outlook365EventId", GetType(String)),
New DataColumn("Outlook365EventUniqId", GetType(String)),
New DataColumn("Outlook365LastChangedUTC", GetType(Date)),
New DataColumn("SchedulerLastChangedUTC", GetType(Date))
})
' Binds the Scheduler Data Storage component to data and defines common mappings.
schedulerDataStorage1.Appointments.DataSource = source
schedulerDataStorage1.Appointments.Mappings.Subject = "Subject"
schedulerDataStorage1.Appointments.Mappings.Description = "Description"
schedulerDataStorage1.Appointments.Mappings.Start = "Start"
schedulerDataStorage1.Appointments.Mappings.End = "End"
' Defines custom mappings.
schedulerDataStorage1.Appointments.CustomFieldMappings.Add(New AppointmentCustomFieldMapping("outlook365_calendar_id", "Outlook365CalendarId"))
schedulerDataStorage1.Appointments.CustomFieldMappings.Add(New AppointmentCustomFieldMapping("outlook365_event_id", "Outlook365EventId"))
schedulerDataStorage1.Appointments.CustomFieldMappings.Add(New AppointmentCustomFieldMapping("outlook365_event_ICalUId", "Outlook365EventUniqId"))
schedulerDataStorage1.Appointments.CustomFieldMappings.Add(New AppointmentCustomFieldMapping("outlook365_lastChangedUTC", "Outlook365LastChangedUTC"))
schedulerDataStorage1.Appointments.CustomFieldMappings.Add(New AppointmentCustomFieldMapping("scheduler_lastChangedUTC", "SchedulerLastChangedUTC"))
DXOutlook365Sync component does not support synchronization of recurring appointments with a changed occurrence type.Object MarshalByRefObject Component DXOutlook365Sync
See Also