You are here:   Blog
Register   |  Login

LightSwitch News

 

Aug 14

Written by: Michael Washington
8/14/2013 7:45 PM  RssIcon

image

The ComponentOne Studio for LightSwitch HTML suite of controls contains an Events Calendar control that allows you to create some really incredible applications. In this example we will use it to build a Visual Studio LightSwitch application that will allow employees to enter their vacations and time off so that their organization can easily track employee availability.

You can find full documentation for all the controls in the suite at this link:

http://helpcentral.componentone.com/nethelp/c1htmlclientlightswitch/

In this tutorial we will not go over every step, so if you are new to Visual Studio LightSwitch HTML Client, it is recommended that you start here: Online Ordering System (An End-To-End LightSwitch Example).

 

image

The first page that opens is designed for mobile devices. When you open it, the default day is today. It allows you to easily see the employees who are off on that day. You can change the start and stop times to see employees who are off between the selected days.

image

Employees (people) can be administered in the application.

image

The demographic data for the employees is stored and administered.

image

Clicking the Vacations tab, when you have an employee selected, allows you to see and edit their vacations.

image

When viewing a vacation you can call or email the employee with a simple click on the links.

image

The ComponentOne Calendar control is designed for desktop use. It shows the entries in a daily view.

 

image

… a weekly view…

image

…a monthly view…

image

..and in a list.

 

image

You can change the start and stop times of entries by dragging the top and bottom of each block, or dragging and dropping a block.

image

Double clicking on a block allows you to edit it. Double clicking on a blank space on the calendar allows you to create a new entry.

Creating the Application

First, you will need to download and install the free trial:

image

http://www.componentone.com/SuperProducts/LightSwitchHTML/

image

In Visual Studio 2012 with the latest service updates (or higher), we create a New Project.

image

We create a Visual Studio LightSwitch HTML Application.

Next, we follow the directions on the ComponentOne site:

Creating a ComponentOne Studio for LightSwitch HTML Project

image

Make sure you follow all directions including: Installing the NuGet Client Package.

image

Next we create People and Vacation tables.

image

We want to prevent a user from creating a Vacation with an invalid date.

On the Vacation table, we select the _Validate method and use the following code:

 

        partial void Vacations_Validate(Vacation entity, EntitySetValidationResultsBuilder results)
        {
            if (entity.StartDate > entity.EndDate)
            {
                results.AddEntityError(
                    String.Format("End Date must be greater than {0}", entity.StartDate)
                    );
            }
        }

 

 

image

To set the default values whenever a Vacation is created, open the Vacation table and select the HTMLClient tab. Then select Write Code then the created method.

Use the following code for the method:

 

myapp.Vacation.created = function (entity) {
    var dtStartDate = new Date();
    dtStartDate.setHours(8, 0, 0);
    var dtEndDate = new Date();
    dtEndDate.setHours(17, 0, 0);
    entity.StartDate = dtStartDate;
    entity.EndDate = dtEndDate;
};

 

image

Now we want to create the query that will be used on the main screen.

We right-click on the Vacations table and select Add Query.

image

We name the query GetVacationsBetweenDates , create StartDate and EndDate parameters, and select PreprocessQuery.

We use the following code for the query (note, you will need to add using System.Linq.Expressions to the top of the class):

 

        partial void GetVacationsBetweenDates_PreprocessQuery(
            DateTime? 
            StartDate, 
            DateTime? 
            EndDate, 
            ref IQueryable<Vacation> query)
        {
            if (StartDate.HasValue && EndDate.HasValue)
            {
                var dtStartDate = StartDate.Value.Date;
                var dtEndDate = EndDate.Value.Date;
                query = query
                    .Where(x =>
                        EntityFunctions.CreateDateTime(
                        x.StartDate.Year,
                        x.StartDate.Month,
                        x.StartDate.Day,
                        0, 0, 0)
                        <= dtEndDate
                        && EntityFunctions.CreateDateTime(
                        x.EndDate.Year,
                        x.EndDate.Month,
                        x.EndDate.Day,
                        0, 0, 0)
                        >= dtStartDate
                        );
            }
            else
            {
                // Return nothing
                query.Where(x => x.Id == 0);
            }
        }

 

Client Side Code

image

We want to create the main screen that will show the employees who are on vacation.

The first step is to make a new screen that we will call Main. When the screen opens, perform the following steps:

  1. Click the Add Data Item button to open the Add Data Item dialog.
  2. Click Query.
  3. Select the GetVacationsBetweenDates query we created earlier.
  4. Click OK.
  5. The Query will appear in the View Model on the left side of the screen designer. StartDate and EndDate properties will be automatically created and hooked to the parameters the query requires.
  6. Drag and drop the query to the screen layout.
  7. The StartDate and EndDate properties will be automatically inserted into the screen layout.

image

Switch the label controls for the Start Date and End Date to Date/Time Pickers.

image

To set the defaults, to pull up the current day, we first select the created method and use the following code:

 

myapp.Main.created = function (screen) {
    // Set defaults
    var dtToday = new Date();
    dtToday.setHours(23, 59, 0);
    var dtTomorrow = new Date();
    dtTomorrow.setDate(dtTomorrow.getDate() + 1);
    dtTomorrow.setHours(0, 0, 0);
    screen.StartDate = dtToday;
    screen.EndDate = dtTomorrow;
    screen.details.displayName = "Vacation Tracker";
};

 

image

Alter the screen layout so that it resembles the image above.

 

Add / Edit Vacations

image

Create a AddEditVaction screen for the Vacation table.

image

Change the screen layout so that it resembles the image above.

image

To display the Email and Phone properties, use the Other Screen Data option.

image

Select the associated People table and then enter a period (“.”) and then select the property.

image

We now want to open the Add / Edit Vacation screen from the Main screen.

Open the Main screen, and add a button to the Command Bar.

image

In the Add Button dialog, select Write my own method and call the method AddVacation_Tap.

image

Right-click on the method in the View Model and select Edit Execute Code.

Use the following code for the method:

 

myapp.Main.AddVacation_Tap_execute = function (screen) {
    myapp.showAddEditVacation(null, {
        beforeShown: function (addEditVacationScreen) {
            // Create new Vacation here so that
            // discard will work.
            var newVacation = new myapp.Vacation();
            addEditVacationScreen.Vacation = newVacation;
        },
        afterClosed: function (addEditVacationScreen, navigationAction) {
            // If the user commits the change,
            // show the new Vacation in View Screen.
            if (navigationAction === msls.NavigateBackAction.commit) {
                // Refresh Query
                screen.GetVacationsBetweenDates.load();
            }
        }
    });
};

 

image

To edit an existing Vacation, click on the List control and select the Item Tap event.

In the Edit ItemTap Action dialog, select Write my own method and click OK.

image

Right-click on the method in the View Model and select Edit Execute Code.

Use the following code for the method:

 

myapp.Main.GetVacationsBetweenDates_ItemTap_execute = function (screen) {
    myapp.showAddEditVacation(null, {
        beforeShown: function (addEditVacationScreen) {
            // edit the selected vacation
            addEditVacationScreen.Vacation = screen.GetVacationsBetweenDates.selectedItem;
        },
        afterClosed: function (addEditVacationScreen, navigationAction) {
            // If the user commits the change,
            // show the new Vacation in View Screen.
            if (navigationAction === msls.NavigateBackAction.commit) {
                // Referesh Query
                screen.GetVacationsBetweenDates.load();
            }
        }
    });
};

 

 

image

We add  BrowsePeople and AddEditperson screens and wire them up to the application.

Note: You can download the complete application on the Download page at: http://lightswitchhelpwebsite.com/Downloads.aspx.

If you are new to Visual Studio LightSwitch HTML Client, it is recommended that you start here: Online Ordering System (An End-To-End LightSwitch Example).

 

image

When we run the application we have the ability to add and edit Vacations.

 

Using The ComponentOne Event Scheduler Control

image

To add the ComponentOne Events Calendar, we create a screen and use the Wijmo Events Calendar Screen template connected to the Vacations table.

image

When we run the application and navigate to the screen we see that the calendar displays with the vacation entries.

Yes, it is that easy!

 

Customizing The ComponentOne Event Scheduler Control

We now desire to implement the following additional functionality:

  • Show the name of the person (from the associated Person table)
  • Allow items to be dragged and dropped to be moved
  • Allows items to be resized to change their dates
  • Allow existing items to be edited by clicking on them
  • Allow new items to be created by double clicking on a blank space on the calendar
  • Only load one month’s worth of data at a time

The good news is that the control is fully customizable and has excellent documentation.

The main documentation for the entire suite of controls is here:

http://helpcentral.componentone.com/nethelp/c1htmlclientlightswitch/

The main documentation of the Events Calendar control is here:

http://helpcentral.componentone.com/nethelp/c1htmlclientlightswitch/#!Documents/wijmoeventscalendars1.htm

Additional documentation for the Events Calendar is here:

http://helpcentral.componentone.com/nethelp/c1htmlclientlightswitch/#!Documents/eventscalendar1.htm

Detailed documentation for the wijevcal (used by the Events Calendar) is here:

http://wijmo.com/docs/wijmo/webframe.html#Eventscalendar.html

Complete detailed API documentation for  the wijevcal is here:

http://wijmo.com/docs/wijmo/webframe.html#Wijmo~jQuery.fn.-~wijevcal.html

 

image

The first step is to change the configuration for the control in the screen designer so that the Id field is the third property passed to the control.

Passing the Id field will allow the control to properly identify the calendar item. We will add code that will perform an asynchronous lookup to pull in the other data for the calendar item such as the associated Person. 

image

Next, we click on the root node in the screen designer, and in the Properties we set the screen to Edit Screen Type.

Make A Custom Query

image

To support only retrieving the data for the current selected month, we click the Edit Query button next to the Vacations collection in the screen’s View Model on the left-hand side of he screen.

image

Edit the query to take a StartDate and EndDate parameter.

Click the back button to return to the screen designer.

image

Use the Add Data Item button to add two Data Items of the type of Date to the screen and connect them to the parameters for the query.

You do not need to add the Data Items to the screen layout.

 

Custom JavaScript Code

image

To write our custom code, we click the Write Code button.

We add the following code to hold our global variables we will need:

 

var _calendarControl;
var _screen;
var _currentMonth = new Date();
var _lastSelectedDate = new Date();

 

Next, we add the following created method that will set the global variables and call the method to load the data for the month:

 

myapp.VacationsCalendar.created = function (screen) {
    // Set _screen global variable
    _screen = screen;
    // Set defaults
    _currentMonth = new Date(1900, 1, 1, 0, 0, 0, 0);
    // Get data for the current date
    GetDataForTheMonth(new Date());
};

 

The following method will load the data for the month:

 

function GetDataForTheMonth(startingDate) {
    // Convert the passed date to the month
    var dtDate = new Date(startingDate);
    var paramMonth = new Date(dtDate.getFullYear(), dtDate.getMonth(), 1, 0, 0, 0);
    _currentMonth = new Date(_currentMonth.getFullYear(), _currentMonth.getMonth(), 1, 0, 0, 0);
    // Only update data if current month has changed
    if (paramMonth < _currentMonth || paramMonth > _currentMonth) {
        // Set _currentMonth
        _currentMonth = new Date(paramMonth);
        // Set the date defaults
        var dtStartDate = new Date(_currentMonth);
        dtStartDate.setMonth(dtStartDate.getMonth());
        dtStartDate.setHours(0, 0, 0);
        var dtEndDate = new Date(_currentMonth);
        dtEndDate.setMonth(dtEndDate.getMonth() + 1);
        dtEndDate.setHours(0, 0, 0);
        _screen.paramStartDate = dtStartDate;
        _screen.paramEndDate = dtEndDate;
        // refresh the collection
        _screen.Vacations.load();
    }
}

 

A _render method already exists. It was created automatically by the ComponentOne plug-in. However, we want to replace it with our custom code:

 

myapp.VacationsCalendar.WijmoGrid_render = function (element, contentItem) {
    $.wijmo.wijcalendar.prototype.options.wijCSS.stateDefault = "ui-btn-up-a";
    $.wijmo.wijcalendar.prototype.options.wijCSS.content = "ui-body ui-body-a";
    _calendarControl = $("<div/>");
    _calendarControl.appendTo($(element));
    _calendarControl.attr("style", "width: 800px; height: 640px");
    setTimeout(function () {
        _calendarControl.wijevcal({
            viewType: "day",
            viewTypeChanged: function (e) {
                $(".wijmo-wijev-headerbar .wijmo-wijev-tools").controlgroup("refresh");
            },
            beforeUpdateEvent: function (e, data) {
                // Get the Vacation Item to be updated
                var filter = "(Id eq " + msls._toODataString(data.data.VacationId, ":Int32") + ")";
                myapp.activeDataWorkspace.ApplicationData.Vacations.filter(filter)
                    .execute().then(function (result) {
                        // Get the results of the query
                        var existingVacation = result.results[0];
                        // Update the vacation
                        existingVacation.StartDate = data.data.start;
                        existingVacation.EndDate = data.data.end;
                    });
            },
            selectedDatesChanged: function (e, data) {
                // Get the minimum date from the array of dates
                var dates = [];
                $.each(data.selectedDates, function (key, value) {
                    dates.push(new Date(value))
                });
                var sorted = dates.sort(function (a, b) {
                    return a.getTime() - b.getTime();
                });
                _lastSelectedDate = sorted[0];
                // Possibly refresh the data for the select month
                GetDataForTheMonth(_lastSelectedDate);
            }
        });
        $(".wijmo-wijev-headerbar .wijmo-wijev-tools").controlgroup("refresh");
        _calendarControl.wijevcal("invalidate");
        refreshCalendar(contentItem);
    }, 100);
    contentItem.value.oncollectionchange = function () {
        refreshCalendar(contentItem);
    };
};

 

The code above calls a refreshCalendar method, we will add that now:

 

function refreshCalendar(calendarContent) {
    var calendar = c1ls.getCalendarContent(calendarContent);
    _calendarControl.wijevcal({
        timeInterval: 60,
        timeIntervalHeight: 20,
        beforeEditEventDialogShow: calendar.Select
    });
    // We want the name of the event to be the name of the 
    // person
    $.each(calendar, function (key, value) {
        if (key == 'Events') {
            // Loop thru each event
            $.each(value, function (key, vacationEvent) {
                // To assign the label for the event to the 
                // name of the person in the associated
                // People table we need to make a few queries
                // The vacationId is the subject
                // Assign it to vacationId
                var vacationId = vacationEvent.subject;
                // So that we can update the Vacation event later
                // Create a VacationId property and set it
                vacationEvent.VacationId = vacationId;
                // For now set the subject to "loading..."
                // It will be updated by the async method (below)
                vacationEvent.subject = "loading...";
                // Create a filter               
                var filter = "(Id eq " + msls._toODataString(vacationId, ":Int32") + ")";
                // Get the Vacation
                myapp.activeDataWorkspace.ApplicationData.Vacations.filter(filter)
                    .execute().then(function (result) {
                        // Get the Person
                        result.results[0].getPerson().then(function (result) {
                            // Set the Person's name
                            vacationEvent.subject = result.Name;
                            // Update the Calendar with the updated data
                            _calendarControl.wijevcal("option", "eventsData", calendar.Events);
                        });
                    });
            });
        }
    });
    if (calendar.Events[0] == null) {
        // Update the Calendar with the updated data
        _calendarControl.wijevcal("option", "eventsData", calendar.Events);
    }
}

 

Editing Calendar Items

image

To allow calendar items to be edited in a popup, we return to the screen designer and click on the Colums layout for the control and select the tap event.

image

We create a method called EditSelectedVacation.

image

We select the method in the View Model and select Edit Execute Code.

We use the following code for the method:

 

myapp.VacationsCalendar.EditSelectedVacation_execute = function (screen) {
    myapp.showAddEditVacation(null, {
        beforeShown: function (addEditVacationScreen) {
            if (screen.Vacations.selectedItem != null) {
                // Edit selected Vacation
                addEditVacationScreen.Vacation = screen.Vacations.selectedItem;
            } else {
                // Create new Vacation
                var newVacation = new myapp.Vacation();
                // Create start and end times
                var dtStartDate = new Date(_lastSelectedDate);
                dtStartDate.setHours(8, 0, 0);
                var dtEndDate = new Date(_lastSelectedDate);
                dtEndDate.setHours(17, 0, 0);
                newVacation.StartDate = dtStartDate;
                newVacation.EndDate = dtEndDate;
                // set new Vacation
                addEditVacationScreen.Vacation = newVacation;
            }
        },
        afterClosed: function (addEditVacationScreen, navigationAction) {
            // If the user commits the change,
            // show the new Vacation in View Screen.
            if (navigationAction === msls.NavigateBackAction.commit) {
                // Save changes
                SaveCalendarChanges();
            }
        }
    });
};

 

That method calls a SaveCalendarChanges method. We add that using the following code:

 

function SaveCalendarChanges() {
    // Save changes
    myapp.applyChanges().then(function () {
        // Referesh Query
        _screen.Vacations.load();
        var calendar = c1ls.getCalendarContent(screen.Vacations);
        // Update the Calendar with the updated data
        _calendarControl.wijevcal("option", "eventsData", calendar.Events);
    }, function fail(e) {
        // If error occurs, show the error.
        msls.showMessageBox(e.message, { title: e.title }).then(function () {
            // Discard Changes
            _screen.details.dataWorkspace.ApplicationData
                .details.discardChanges();
        });
    });
}

 

Download Code

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

(you must have Visual Studio 2012 (or higher) installed and the ComponentOne Studio for LightSwitch HTML plug-ins to run the code)


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