Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

LightSwitch: Integrating The ComponentOne Scheduler

0.00/5 (No votes)
8 Dec 2011 1  
The point of this article is to demonstrate methods you can use when faced with integrating a complex control.

Databinding A Complex Multi-Table Silverlight Control In LightSwitch: ComponentOne Scheduler

Introduction

In this article, we will tackle a complex multi-table control in their Silverlight Scheduler Control. This is a full featured control that has a ton of functionality. It is definitely worth the effort to make it work with LightSwitch. The point of this article is to demonstrate methods you can use when faced with integrating a complex control.

Background

A strong point about ComponentOne is their ability to make great Silverlight controls that aggregate data. The LightSwitch Help Website previously covered their LightSwitch extension: Using OLAP for LightSwitch. That control is designed to be mostly configured by end-users. The article: The LightSwitch Control Extension Makers Dilemma explains how LightSwitch Control Extensions are easy to use, but have limited configuration.

The reason the control is so complex is that it communicates with several tables at the same time. It allows you to store Contacts, Resources, and Appointments. When incorporated into your own LightSwitch application, these tables would point to your data. Using this control, you could, for example, create an application that allowed employees to reserve meeting rooms.

LightSwitch

The Scheduler control is a normal Silverlight control. What LightSwitch 'brings to the table' is the ability to create the application in minutes. Once I read the directions for the control, I was able to create the tables needed in 5 minutes. At that point, 90% of the application was done.

The Scheduler Control

image

When you run the sample (you can download the code on the downloads page), you will see a popup indicating you are using the evaluation version.

Click the ‘X’ to close the popup.

image

Double-click on a day to open a popup that allows you to create or edit an Appointment.

image

You can also indicate Resources to attach to the Appointment. You can select one of the existing Resources, or add and delete Resources.

image

You can also indicate Contacts to attach to the Appointment. You can select one of the existing Contacts, or add and delete Contacts.

image

The same options are available for Categories. In this example, we turned off the ability to add new Categories.

image

The Scheduler Control really shines in its ability to group this data and show calendars by Contact

image

…or Resource

image

…or Category.

image

It also displays data by Day, Week, and Working Week.

image

You can create recurring appointments.

image

It will even pop up reminders.

More On The Scheduler Control

You can see a live demonstration of all of ComponentOne’s controls at this link: http://demo.componentone.com/Silverlight/ControlExplorer/

You can get full documentation on the Scheduler Control at: http://helpcentral.componentone.com/nethelp/c1schedulerSL/.

However, if you download and install the Demo Package

image

… and click on the Samples that are installed…

image

You will find ready to run projects. However, these are normal Silverlight projects, so you need to know how to bind them in LightSwitch. The good news is that as we demonstrated, LightSwitch is capable of working with any Silverlight Control no matter how complex.

Implementing The Control

image

The entire Solution consists of three Entities (tables), and a Silverlight project that contains the Schedule Control. The Schedule Control is the only item placed on the Calendar Screen which is the only Screen in the application.

image

image

image

The schema for the Entities was determined by looking at the data sources, and the fields, that the control is designed to use (and by reading the documentation and looking at the sample code).

The Silverlight Project

image

(Note: See: Creating A LightSwitch Custom Silverlight Control for step-by-step directions on implementing a Silverlight Control in LightSwitch).

In the Silverlight project, we add references to the ComponentOne assemblies and the LightSwitch assemblies. Also note that when the Control is placed on the LightSwitch Screen, the same assemblies need to be referenced again. For an example of this, see the steps used to implement this control: Using the Telerik Rich Text Editor In Visual Studio LightSwitch.

There is a lot of XAML borrowed from the sample ComponentOne code to display the drop downs and buttons, but the following XAML is all that is used to display the actual Scheduler Control:

<c1:C1Scheduler Grid.Row="2" Grid.Column="1" 
                x:Name="sched1" 
                GroupPageSize="2" 
                StyleChanged="sched1_StyleChanged" 
                UserDeletingAppointment="sched1_UserDeletingAppointment">
    <c1sched:C1Scheduler.Settings>
        <c1sched:C1SchedulerSettings 
            AllowContactsEditing="True"
            AllowResourcesEditing="True"
            AllowCategoriesEditing="False" 
            AllowCategoriesMultiSelection="True" 
            AllowResourcesMultiSelection="True" 
            AllowContactsMultiSelection="True" 
            FirstVisibleTime="08:00:00" />
    </c1sched:C1Scheduler.Settings>
</c1:C1Scheduler>

Normally we would have binding code in the XAML, however, this control throws errors when we do that because LightSwitch will initially bind to a control but not actually pass any data.

Normally with binding, if a binding is bad or in error, it silently fails. The Scheduler Control is complex and throws an error. We therefore code the binding in code behind to resolve this issue.

Simply, we will not actually set any binding until we know that LightSwitch actually has data to supply (yes, this may also be possible to accomplish using Silverlight Value Converters).

We wire up a property that will call the following method when LightSwitch binds data to the control:

 private static void OnScheduleControlPropertyChanged(
    DependencyObject d, DependencyPropertyChangedEventArgs e)
{
    // Get the DataContext
    ScheduleControl objScheduleControl = (ScheduleControl)d;
    IContentItem contentItem = (IContentItem)objScheduleControl.DataContext;
    if (contentItem.Details != null)
    {
        // Fill the Group drop down
        objScheduleControl.cmbGroup.Items.Add("None");
        objScheduleControl.cmbGroup.Items.Add("Category");
        objScheduleControl.cmbGroup.Items.Add("Resource");
        objScheduleControl.cmbGroup.Items.Add("Contact");
        objScheduleControl.cmbGroup.SelectedIndex = 0;
        
        // Fill the Views drop down
        objScheduleControl.cmbView.Items.Add("Day");
        objScheduleControl.cmbView.Items.Add("Working Week");
        objScheduleControl.cmbView.Items.Add("Week");
        objScheduleControl.cmbView.Items.Add("Month");
        objScheduleControl.cmbView.SelectedIndex = 3;
        
        // Set mapping between ContactStorage and Contact table columns
        BaseObjectMappingCollection<Contact> ContactMappings =
            objScheduleControl.sched1.DataStorage.ContactStorage.Mappings;
        ContactMappings.IndexMapping.MappingName = "Id";
        ContactMappings.IdMapping.MappingName = "GuidId";
        ContactMappings.CaptionMapping.MappingName = "Caption";
        ContactMappings.ColorMapping.MappingName = "Color";        
        ContactMappings.TextMapping.MappingName = "Text";
        objScheduleControl.sched1.DataStorage.ContactStorage.DataMember = "Contacts";
        objScheduleControl.sched1.DataStorage.ContactStorage.DataSource = contentItem.Screen;
        
        // Set mapping between ResourceStorage and Resource table columns
        BaseObjectMappingCollection<Resource> ResourceStorage =
            objScheduleControl.sched1.DataStorage.ResourceStorage.Mappings;
        ResourceStorage.IndexMapping.MappingName = "Id";
        ResourceStorage.IdMapping.MappingName = "GuidId";
        ResourceStorage.CaptionMapping.MappingName = "Caption";
        ResourceStorage.ColorMapping.MappingName = "Color";
        ResourceStorage.TextMapping.MappingName = "Text";
        objScheduleControl.sched1.DataStorage.ResourceStorage.DataMember = "Resources";
        objScheduleControl.sched1.DataStorage.ResourceStorage.DataSource = contentItem.Screen;
        
        // Set mapping between AppointmentStorage and Appointment table columns
        AppointmentMappingCollection AppointmentMappings = 
            objScheduleControl.sched1.DataStorage.AppointmentStorage.Mappings;
        AppointmentMappings.IndexMapping.MappingName = "Id";
        AppointmentMappings.IdMapping.MappingName = "GuidId";
        AppointmentMappings.Subject.MappingName = "Subject";
        AppointmentMappings.Body.MappingName = "Body";
        AppointmentMappings.End.MappingName = "TimeEnd";
        AppointmentMappings.Start.MappingName = "TimeStart";
        AppointmentMappings.Location.MappingName = "Location";
        AppointmentMappings.AppointmentProperties.MappingName = "Properties";
        
        objScheduleControl.sched1.DataStorage.AppointmentStorage.DataMember = "Appointments";
        objScheduleControl.sched1.DataStorage.AppointmentStorage.DataSource = 
                            contentItem.Screen;  
    }

Note that we also indicate how each data source is mapped to our Entities in LightSwitch.

Every control is different, so the important concepts are that you can get an instance of the LightSwitch DataContext, check that it actually has data, and bind to your Silverlight control.

Trouble Deleting

With complex controls, you may run into problems where the control does not communicate with LightSwitch in the way it expects, and you get errors. This happens when you attempt to delete an Appointment.

When the Scheduler Control deletes an Appointment, it is necessary to delete the record by locating it by GUID, and explicitly deleting it in LightSwitch.

This is the code that is used:

private void sched1_UserDeletingAppointment(object sender, 
            C1.Silverlight.Schedule.AppointmentActionEventArgs e)
{
    // Get a reference to the LightSwitch DataContext 
    var objDataContext = (IContentItem)this.DataContext;
    // Get a reference to the LightSwitch Screen
    var Screen =
                (Microsoft.LightSwitch.Client.IScreenObject)objDataContext.Screen;
    // Delete the appointment in LightSwitch
    Screen.Details.Dispatcher.BeginInvoke(() =>
    {
        string strGuid = Convert.ToString(e.Appointment.Key[0]);
        Guid DeletedGuidId = new Guid(strGuid);
        LightSwitchApplication.DataWorkspace DataWorkspace = 
            (Screen.Details.DataWorkspace as LightSwitchApplication.DataWorkspace);
        LightSwitchApplication.Appointment DeletedAppointment = 
            DataWorkspace.ApplicationData.Appointments
            .Where(x => x.GuidId == DeletedGuidId).FirstOrDefault();
        if (DeletedAppointment != null)
        {
            DeletedAppointment.Delete();
        }
    });
} 

A new instance of the EntityObject class cannot be initialized because the ambient IDataWorkspace is not available. Please use the constructor that specifies an EntitySet.

image

If you have arrived at this blog article because you received the error above, the solution is to perform whatever action you are trying to do in the LightSwitch Screen code behind (rather than in the Silverlight Control its self).

image

The first step we perform is to add a public property in the Silverlight Control that will allow the LightSwitch Screen code to access the Silverlight Control programmatically.

We then use the following in the LightSwitch Screen code:

using System;
using Microsoft.LightSwitch.Presentation;
using Microsoft.LightSwitch.Presentation.Extensions;
using SilverlightLibrary;
namespace LightSwitchApplication
{
    public partial class Calendar 
    {
        // The following methods are by Raleigh Johnson 
        // http://ComponentOne.com
        partial void Calendar_Created()
        {
            // Get an instance of the ComponentOne Scheduler Control
            IContentItemProxy schedProxy = this.FindControl("Appointments");
            // Create a handler to fire when the control is actually available            
            schedProxy.ControlAvailable +=
   new EventHandler<ControlAvailableEventArgs>(schedProxy_ControlAvailable);
        }

        void schedProxy_ControlAvailable(object sender, ControlAvailableEventArgs e)
        {
            // Get an instance of the ComponentOne Scheduler Control
            ScheduleControl sc = e.Control as ScheduleControl;
            // Create an event that will fire when an Appointment is being created
            sc.Scheduler.DataStorage.AppointmentStorage.AddingNew += (obj, args) =>
            {
                // Create a new Appointment in LightSwitch
                Appointment app = new Appointment
                (this.DataWorkspace.ApplicationData.Appointments);
                // Set the NewObject in the ComponentOne Scheduler Control 
                args.NewObject = app;
            };
            // Create an event that will fire when a Contact is being created
            sc.Scheduler.DataStorage.ContactStorage.AddingNew += (obj, args) =>
            {
                // Create a new Contact in LightSwitch
                Contact app = new Contact(this.DataWorkspace.ApplicationData.Contacts);
                // Set the NewObject in the ComponentOne Scheduler Control 
                args.NewObject = app;
            };
            // Create an event that will fire when a Resource is being created
            sc.Scheduler.DataStorage.ResourceStorage.AddingNew += (obj, args) =>
            {
                // Create a new Resource in LightSwitch
                Resource app = new Resource(this.DataWorkspace.ApplicationData.Resources);
                // Set the NewObject in the ComponentOne Scheduler Control 
                args.NewObject = app;
            };
        }
    }
}

Most Controls Won't Be This Hard

I decided to tackle this control because I knew it would be hard. My hope was to demonstrate that LightSwitch is capable of handling complex business applications. Anything that can be done in a normal Silverlight application can be done in LightSwitch.

Special Thanks

This article would not have been possible without the assistance of ComponentOne.

An extra special thank you to Raleigh Johnson of ComponentOne for providing code required to properly create new Appointments.

I would also like to thank Rich Dudley of ComponentOne.

More LightSwitch Scheduler Controls

Paul Patterson has a tutorial on using the Telerik Scheduler here:

More LightSwitch Tutorials Are On The LightSwitchHelpWebsite.com

http://LightSwitchHelpWebsite.com

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here