You are here:   Blog
Register   |  Login

 

Oct 13

Written by: Michael Washington
10/13/2011 10:44 PM  RssIcon

image

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.

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.

The reason the control is so complex, is that is 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.

Note: Also see A Groundbreaking Control - ComponentOne Scheduler LightSwitch Extension (for a version of this control that does not require any code)

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.

 

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 post 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;
            };
        }
    }
}

 

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.

 

Most Controls Wont 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.

Note: Also see A Groundbreaking Control - ComponentOne Scheduler LightSwitch Extension (for a version of this control that does not require any code)

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:

http://www.paulspatterson.com/technology/lightswitch/ls-telerik-radscheduler/

 

Download Code

The LightSwitch project is available at http://lightswitchhelpwebsite.com/Downloads.aspx

10 comment(s) so far...


Gravatar

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

Did you figure out yet how to bring Doctors into the UI as Appointment Owner's for grouping? I saw you had created a table for Doctors, but the LightSwitch auto-generated foreign key name of Appointment_Doctor is giving me fits.

AppointmentMappings.OwnerIndexMapping.MappingName = "Appointment_Doctor"

doesn't seem to work. I have a similar application of this for a Property Management office with several Partners. The MultiUser sample gives hints but I haven't found the key to wire it up yet.

By Bob Baker on   10/15/2011 4:46 AM
Gravatar

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

@Bob Baker - I didn't try to use Owners, but I did connect to Contacts and the pattern should be the same, see how I mapped to my Contacts table.

By Michael Washington on   10/15/2011 4:49 AM
Gravatar

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

I don't think it's the same. Owner is a 2011 v2 addition and is essentially a special type of Contact (also has GuidId, Text, Caption, and Color), and the MultiUser example uses a foreign key in the Appointments table explicitly by setting the property I indicated above. In LightSwitch, that foreign key would be named Appointment_Doctor in your case, or Appointment_Owner in mine (I hadn't seen your Doctor table before I created an Owner table). I had started out with just copying the Contacts mapping, and changing it to the OwnersStorage and Mapping, but you need that additional step of setting the OwnerIndexMapping (or OwnerIdMapping if using Guids) pointing to the foreign key in the Appointments datasource. I have since got the 'Owners' loading like the MultiUser example, but save does not appear to pass thru. You did not explicitly implement delete as far as I could tell in your example. I implemented an AppointmentAdded event handler and the new Appointment gets saved to the Appointments table, but does not show up in the Scheduler (possibly because I did not implement any XML Properties). As you might suspect, the MultiUser example does not implement any CUD. Back to the docs...

By Bob Baker on   10/15/2011 2:10 PM
Gravatar

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

@Bob Baker - Start a thread in the forums, it would make it easier to work this out. I don't have a Doctor table, also I did explicitly implement the delete :) My example handles the XML Properties automatically, are you looking at my code? Again, please post to the forums, the comments section in this blog is horrible for a conversation.

By Michael Washington on   10/15/2011 2:13 PM
Gravatar

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

Michael, I have 3 questions
1) How long did it take to prepare this?
2) Related to the first, do you believe (or maybe know) it's going to be released as a LS extension? Is complexity prohibiting?
2) If you were asked to suggest a scheduler control (I am interested in acquiring a commercial solution in the near future) what would be the top 3 (if you can answer)?

Thanks in advance

By kchristo on   10/16/2011 12:51 AM
Gravatar

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

@kchristo
1) It took hours to figure out, but now that I know more I could create this in 15 minutes
2) I don't know but it could
3) All I can say is at this time this one would be my number one

By Michael Washington on   10/16/2011 5:24 AM
Gravatar

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

Hello, I have one bug.

How we can get an instance of the ComponentOne Scheduler Control?


In LightSwitch I have :

ScheduleControl sc = e.Control as ScheduleControl;

// Create an event that will fire when an Appointment is being created

sc.Sheduler.DataStorage.AppointmentStorage.AddingNew += (obj, args) => ............


But I get the error:

C1.Silverlighjt.Schedule.C1Scheduler does not contain a definition for "DataStorage"......


Any ideia?
Thanks


By mwilson on   12/22/2011 5:23 AM
Gravatar

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

@mwilson - See: http://lightswitchhelpwebsite.com/Forum/tabid/63/aft/454/Default.aspx

By Michael Washington on   12/22/2011 6:16 AM
Gravatar

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

LightSwitchApplication.DataWorkspace DataWorkspace =
(Screen.Details.DataWorkspace as LightSwitchApplication.DataWorkspace);



Hi Michael

How is that line of code in the above version of Lighswtich contained in VS 2013?

Apparently something changed in the disposition of this dll's causing me to have problems at this point, could you help me?

By Victor Perez on   7/23/2014 8:12 PM
Gravatar

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

@Victor Perez - I am sorry I have not done any Silverlight development in years. I honestly do not know the answer. However, a post on the official Microsoft Forums should find you some help: http://social.msdn.microsoft.com/Forums/vstudio/en-US/home?forum=lightswitch

By Michael Washington on   7/23/2014 9:26 PM

Your name:
Gravatar Preview
Your email:
(Optional) Email used only to show Gravatar.
Your website:
Title:
Comment:
Security Code
CAPTCHA image
Enter the code shown above in the box below
Add Comment   Cancel 

Microsoft Visual Studio is a registered trademark of Microsoft Corporation / LightSwitch is a registered trademark of Microsoft Corporation