New to Telerik UI for WinForms? Download free 30-day trial

How to Display Multiple Resources in EditAppointmentDialog

Environment

Product Version Product Author
2020.3.1020 RadScheduler for WinForms Desislava Yordanova

Description

By default, each Appointment has two properties: ResourceId and ResourceIds. Usually, the ResourceId is used when you have only a single resource for the appointment. In case you have multiple resources for an appointment the ResourceIds collection is used. However, the current implementation of the EditAppointmentDialog uses a RadDropDownList for the resource selection. Thus, the user is not allowed to create an appointment and assign two resources to it. It can be achieved only programmatically.

This is a common scenario when creating a meeting and you have at least two participants.

scheduler-multiple-resource-in-edit-dialog 001

Solution

A possible solution would be to replace the resources RadDropDownList with a RadCheckedDropDownList. Thus, the user will be able to select multiple resources. We will follow the demonstrated approach in this article in order to add the RadCheckedDropDownList: Adding a Custom Field to the EditAppointment Dialog.

1. Create a derivative of EditAppointmentDialog and add a RadCheckedDropDownList just next to the default resources RadDropDownList:

scheduler-multiple-resource-in-edit-dialog 002

Use the following code:

Custom EditAppointmentDialog


public partial class CustomEditAppointmentDialog : EditAppointmentDialog
{
    public CustomEditAppointmentDialog()
    {
        InitializeComponent();
    }

    protected override void LoadSettingsFromEvent(Telerik.WinControls.UI.IEvent sourceEvent)
    {
        base.LoadSettingsFromEvent(sourceEvent);

        foreach (EventId resourceId in sourceEvent.ResourceIds)
        {
            foreach (RadCheckedListDataItem item in this.radCheckedDropDownList1.Items)
            {
                if (((EventId)item.Value).KeyValue.Equals(resourceId.KeyValue))
                {
                    item.Checked = true;
                }
            }
        }
    }

    protected override void ApplySettingsToEvent(Telerik.WinControls.UI.IEvent targetEvent)
    {
        base.ApplySettingsToEvent(targetEvent);

        targetEvent.ResourceIds.Clear();
        foreach (RadCheckedListDataItem item in this.radCheckedDropDownList1.Items)
        { 
            if (item.Checked)
            {
                targetEvent.ResourceIds.Add(item.Value as EventId);
            }
        }
    }

    protected override void LoadResources()
    {
        base.LoadResources();
        this.cmbResource.Visible = false;
        this.radCheckedDropDownList1.Top = this.cmbResource.Top;
        this.radCheckedDropDownList1.Left = this.cmbResource.Left;

        ISchedulerStorage<IResource> resourceStorage = this.SchedulerData.GetResourceStorage();
        foreach (IResource resource in resourceStorage)
        {
            if (resource.Visible)
            {
                RadListDataItem item = new RadListDataItem(resource.Name);
                item.Value = resource.Id;
                this.radCheckedDropDownList1.Items.Add(item);
            }
        }
    }
}

2. This is how the scheduler is bound to custom objects:

This example can be adopted to other data binding scenarios as well. However, the main principle is the same. In the EditAppointmentDialog you should read the resources from the Appointment and check the relevant resource items in the checked drop down. When you toggle/untoggle a resource item, then you should store the correct data to the Appointment being edited. The LoadSettingsFromEvent and ApplySettingsToEvent methods are used for this purpose.


private void SchedulerSetup()
{
    SchedulerBindingDataSource dataSource = new SchedulerBindingDataSource();

    BindingList<CustomResource> resources = new BindingList<CustomResource>();
    for (int i = 0; i < 5; i++)
    {
        CustomResource resource = new CustomResource();
        resource.Id = i;
        resource.Name = "Resource " + i;
        resources.Add(resource);
    }
    ResourceMappingInfo resourceMappingInfo = new ResourceMappingInfo();
    resourceMappingInfo.Name = "Name";
    resourceMappingInfo.Id = "Id";
    dataSource.ResourceProvider.Mapping = resourceMappingInfo;
    dataSource.ResourceProvider.DataSource = resources;

    BindingList<CustomAppointment> appointments = new BindingList<CustomAppointment>();
    for (int i = 0; i < 10; i++)
    {
        if ((i % 2) == 0)
        {
            int appointmentNumber = i + 1;
            CustomAppointment myAppointment =
                new CustomAppointment(
                    DateTime.Now.AddHours(appointmentNumber),
                    DateTime.Now.AddHours(appointmentNumber + 2),
                    "Appointment " + appointmentNumber.ToString(),
                    "Description for Appointment " + appointmentNumber.ToString(),
                    "Conference room " + appointmentNumber.ToString()); 
            myAppointment.Resources.Add(new EventId(resources[i % 3].Id));
            myAppointment.Resources.Add(new EventId(resources[i % 3 + 1].Id));
            appointments.Add(myAppointment);
        }
    }
    AppointmentMappingInfo appointmentMappingInfo = new AppointmentMappingInfo();
    appointmentMappingInfo.Start = "Start";
    appointmentMappingInfo.End = "End";
    appointmentMappingInfo.Summary = "Subject";
    appointmentMappingInfo.Description = "Description";
    appointmentMappingInfo.Location = "Location";
    appointmentMappingInfo.UniqueId = "Id";
    appointmentMappingInfo.Exceptions = "Exceptions";
    appointmentMappingInfo.Resources = "Resources";
    dataSource.EventProvider.Mapping = appointmentMappingInfo;
    dataSource.EventProvider.DataSource = appointments;

    this.radScheduler1.DataSource = dataSource;

    this.radScheduler1.GroupType = GroupType.Resource;
}

public class CustomAppointment : INotifyPropertyChanged
{
    private DateTime start = DateTime.Now;
    private DateTime end = DateTime.Now;
    private string subject = string.Empty;
    private string description = string.Empty;
    private string location = string.Empty;
    private Guid id = Guid.NewGuid();
    private List<CustomAppointment> exceptions; 
    private List<EventId> resources = new List<EventId>();

    public CustomAppointment()
    {
    }

    public CustomAppointment(DateTime start, DateTime end, string subject, string description, string location)
    {
        this.start = start;
        this.end = end;
        this.subject = subject;
        this.description = description;
        this.location = location;
        List<CustomAppointment> exceptions = new List<CustomAppointment>();
    }

    public List<CustomAppointment> Exceptions
    {
        get
        {
            return this.exceptions;
        }
        set
        {
            if (this.exceptions != value)
            {
                this.exceptions = value;
                this.OnPropertyChanged("Exceptions");
            }
        }
    }

    public Guid Id
    {
        get
        {
            return this.id;
        }
        set
        {
            if (this.id != value)
            {
                this.id = value;
                this.OnPropertyChanged("Id");
            }
        }
    }

    public DateTime Start
    {
        get
        {
            return this.start;
        }
        set
        {
            if (this.start != value)
            {
                this.start = value;
                this.OnPropertyChanged("Start");
            }
        }
    }

    public DateTime End
    {
        get
        {
            return this.end;
        }
        set
        {
            if (this.end != value)
            {
                this.end = value;
                this.OnPropertyChanged("End");
            }
        }
    }

    public string Subject
    {
        get
        {
            return this.subject;
        }
        set
        {
            if (this.subject != value)
            {
                this.subject = value;
                this.OnPropertyChanged("Subject");
            }
        }
    }

    public string Description
    {
        get
        {
            return this.description;
        }
        set
        {
            if (this.description != value)
            {
                this.description = value;
                this.OnPropertyChanged("Description");
            }
        }
    }

    public string Location
    {
        get
        {
            return this.location;
        }
        set
        {
            if (this.location != value)
            {
                this.location = value;
                this.OnPropertyChanged("Location");
            }
        }
    }

    public List<EventId> Resources
    {
        get
        {
            return this.resources;
        }
        set
        {
            if (this.resources != value)
            {
                this.resources = value;
                this.OnPropertyChanged("Resources");
            }
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;

    protected virtual void OnPropertyChanged(string propertyName)
    {
        if (this.PropertyChanged != null)
        {
            this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}

public class CustomResource : INotifyPropertyChanged
{
    private int id;
    private string name;

    public int Id
    {
        get
        {
            return this.id;
        }
        set
        {
            if (this.id != value)
            {
                this.id = value;
                this.OnPropertyChanged("Id");
            }
        }
    }

    public string Name
    {
        get
        {
            return this.name;
        }
        set
        {
            if (this.name != value)
            {
                this.name = value;
                this.OnPropertyChanged("Name");
            }
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;

    protected virtual void OnPropertyChanged(string propertyName)
    {
        if (this.PropertyChanged != null)
        {
            this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}

3. Handle the AppointmentEditDialogShowing event and replace the default dialog with the custom one:


private void radScheduler1_AppointmentEditDialogShowing(object sender, AppointmentEditDialogShowingEventArgs e)
{
    e.AppointmentEditDialog = new CustomEditAppointmentDialog();
}