Back to Devexpress

DXOutlook365Sync Class

windowsforms-devexpress-dot-xtrascheduler-dot-microsoft365calendar-dot-dxoutlook365sync.md

latest28.9 KB
Original Source

DXOutlook365Sync Class

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

Declaration

csharp
public class DXOutlook365Sync :
    Component
vb
Public Class DXOutlook365Sync
    Inherits Component

Remarks

Examples

The following GitHub examples demonstrate how to synchronize user appointments with Microsoft 365 calendars (import, export, merge):

Get Started

Install the following dependency libraries (NuGet packages):

  1. Azure.Identity 1.11.0+
  2. HtmlAgilityPack 1.11.46
  3. Microsoft.Graph 4.49.0

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.

csharp
using DevExpress.Xpf.Scheduling;
using DevExpress.XtraScheduler.Microsoft365Calendar;

// ...
public partial class MainWindow : Window {
    DXOutlook365Sync dXOutlook365Sync;
    public MainWindow() {
        InitializeComponent();
        dXOutlook365Sync = new DXOutlook365Sync(uiScheduler.CreateStorageAdapter());
        //...
    } 
    // ...
}
vb
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

Initialize DXOutlook365Sync

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:

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:

csharp
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);
}
vb
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.

csharp
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);
}
vb
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

Microsoft 365 Calendars

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.

Enable or Disable Synchronization for Calendars

The DXOutlook365Sync component includes the following methods to enable or disable synchronization for calendars:

Reload 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.

Import Microsoft 365 Events

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.

csharp
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();
}
vb
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

Export User Appointments

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.

csharp
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);
}
vb
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

Merge Calendars

Use the following methods to merge the Scheduler control with Microsoft 365 calendars:

Skip Unwanted Appointments and Events

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.

csharp
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;
}
vb
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

Resolve Merge Conflicts

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.

csharp
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;
}
vb
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.

Customize Appointment or Event Before Synchronization

Handle the following events to customize a user appointment or event before synchronization:

  • CustomizeAppointmentToEvent — Allows you to customize an event when the corresponding appointment is exported (or merged) to a Microsoft 365 calendar.
  • CustomizeEventToAppointment — Allows you to customize an appointment when the corresponding event is imported (or merged) from a Microsoft 365 calendar.

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.

csharp
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);
}
vb
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

Save Changes to Data Source

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 NameProperty Type
outlook365_calendar_idSystem.String
outlook365_event_idSystem.String
outlook365_event_ICalUIdSystem.String
outlook365_lastChangedUTCSystem.DateTime
scheduler_lastChangedUTCSystem.DateTime

The following example demonstrates how to create a DataTable object with special fields and define custom mappings:

csharp
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"));
vb
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"))

Limitations

Inheritance

Object MarshalByRefObject Component DXOutlook365Sync

See Also

Synchronization with Microsoft 365 Calendars

DXOutlook365Sync Members

DevExpress.XtraScheduler.Microsoft365Calendar Namespace