After using the Visual Studio LightSwitch HTML Client, you may wonder, how does it work? How does it turn the program we designed into an actual web application?…
After using the Visual Studio LightSwitch HTML Client, you may wonder, how does it work? How does it turn the program we designed into an actual web application?
We will explore the application created in the tutorial: Online Ordering System (An End-To-End LightSwitch Example).
The tool that will be most helpful will be Fiddler, available here: http://fiddler2.com. This program will allow us to monitor the traffic that is transmitted between the web server running the LightSwitch application and our web browser.
When we run Fiddler and monitor the web traffic created by the End-To-End application (running at: https://endtoendexample.lightswitchhelpwebsite.com/HTMLClient), we see that is loads a number of files.
Note: the 404 errors in the image above are due to the sample application not employing any Localization.
All of these files are required to display the first page. We will also see that as we use the application, it will transmit only a small amount of information. Primarily, only the data required to display or update the application.
Everything starts with the HTML page.
When we look at the contents of the page, we see that it loads the .css files and .js scripts that the LightSwitch application requires.
The JavaScript library files that it loads are:
The msls.js library is the heart of the LightSwitch framework. There is an enormous amount of functionality in this file. Much of it you can reference in your custom code.
The best way to explore it, is to open the msls-vsdoc.js file in the Scripts directory and read the comments.
There is a project by Soft Landing, inc that is documenting this library. You can get more information about that project at this link: http://softlandingcanadaforums.com/yaf_postsm49_HTML-Client-API-Discovery-and-Extension-Framework.aspx.
During the application start-up process, the msls.js library makes a call to the SecurityData.svc service to determine what the authentication type for the application is. If Forms Authentication is detected, the LightSwitch client will display the logout button.
The generatedAssets.js file is referenced in the default.htm page.
It contains code that loads other required JavaScript libraries and files.
The usercode.js file (loaded by the generatedAssets.js file) loads the JavaScript files that contain custom code that we have created.
In the LightSwitch Silverlight client, the structure of the application is contained in .lsml files. In the running LightSwitch HTML Client, the structure of the application is contained in the model.json file.
The model.json file is created each time we build the LightSwitch application.
The file is in JSON format and describes the entire application. When this is loaded, the application has all the information it needs to run, except for the data.
When we look at a screen in the LightSwitch designer, we see that it consists of Properties, Collections and Commands. This is consistent with the MVVM (Model-View-View Model) structure of the Silverlight Client.
When we look at the same screen in the model.json file we see that it defines the structure of the screens.
The viewModel.js library defines the JavaScript representation of the screens and allows programmatic access to the screens.
The data.js library contains a representation of all the data assets in the LightSwitch application.
It also contains the location of the .svc services that are used to actually retrieve the data from the server.
The msls.js library, at this point, has access to all the information needed to obtain the data needed by the application.
In this example, it knows from the model.json library that the first page contains the orders collection. It constructs a query using properties from the data.js library to retrieve the data.
The application is very efficient at this point. It only needs to contact the server to retrieve data and images.
Clicking the save button, when adding a single order detail…
… causes only one small request to the server.
Inserting, or updating in the application requires only a small data payload.
After inserting and updating several records, only the data service calls are needed.
A very special thanks to LightSwitch team member Stephen Provine for his valuable assistance.
The best way to measure growth on the LightSwitchHelpWebsite.com is by looking at the number of members who join each month. Currently there are over 6000 members.
As you can see from the chart, growth was declining for years. I attribute this to the uncertainty around Silverlight. In the past, LightSwitch only created Silverlight applications.
This year, the Visual Studio LightSwitch team released the LightSwitch HTML Client.
As you can see from the chart, things have changed dramatically
The Microsoft Visual Studio LightSwitch team was a bit disappointed when I finally walked through the door. I was late to the invite only meeting where they revealed the beta of the new Visual Studio LightSwitch HTML Client. Yes, as long suspected, Visual Studio LightSwitch, the tool that previously only created Silverlight applications (Silverlight by this time was already facing huge challenges in its adoption because it did not run on any of the popular mobile devices), will now create HTML pages.
My flight from Los Angeles to Seattle was a bit late. I was so late in fact that the group was on their scheduled break. The team rolled back the slides and got me caught up. We get to the part about the HTML Client and I think the first words out of my mouth were, “Wow, that is great!” followed by “What about desktop?”.
The LightSwitch team is too nice to actually slap their palms to their foreheads in front of me and yell “Doh! he doesn’t get it!” but, really they should have. I didn’t get it. They calmly explained that they were “targeting mobile devices” because mobile is growing and creating applications that work well on mobile devices is really important. This may seem obvious now, however this conversation took place years ago.
I have worked with the LightSwitch HTML Client for a few years now (today is was finally released). What I am only now understanding is that my desire to have it create a desktop HTML application was misguided. It’s like a person who only understands steam engines trying to understand a car (“That’s nice but it is too small to carry enough coal!”).
The main thing I needed to realize, was that if the primary purpose is to create the best HTML applications for the end user, their design is the best way to achieve that… whether you are using a mobile or a desktop HTML web browser.
The articles that you want to read are: A New User Experience (Heinrich Wendel) and: Designing for Multiple Form Factors (Heinrich Wendel).
This article shows how a LightSwitch application allows the end user to click on a screen element and quickly display a popup that allows them to view details or edit data. In a desktop application, the end-user will see the screen greyed out behind the popup. This allows the user to easily mentally track where they came from (and where they will return to).
If the user is using a tablet or a phone, the application will display only the popup (it will display the previous screen when they close the popup).
Also note that if a user is using a tablet, it will display the entire popup with both its columns, however, if the user is using a phone, it will display only one column at a time. The user will scroll down to get to all the information.
This is important, because we can create one application that works on all devices!
The Visual Studio LightSwitch HTML Client creates Single Page Applications. This design essentially means that when a end-user arrives at your LightSwitch application, a thin JavaScript framework loads and it communicates with the back-end services using OData.
Most importantly, you don’t have the slow postbacks that while they are merely annoying on a desktop application, make the same application practically unusable on a mobile device (because they move so slow due to being run on less powerful web browsers and slower internet connections). Instead of postbacks, only the data is transferred back and forth when a user views and saves data. The screen is not redrawn each time. This makes for a much faster application.
Let’s look at an example of what I am talking about. I have a popular open source help desk ticket tracking application project called ADefHelpDesk. My plan is to make a new version using the LightSwitch HTML Client. My original thinking was to recreate the existing HTML screens. Now, it can be done, but I no longer believe it is a good idea and that is not what I plan to do.
Looking at the screen above, there are a lot of things on the page that do not need to be there. The reason they are there is that it takes so long for data to be displayed, I want to display as much information as possible. I also want all possible buttons and switches available without the need for the user to click-click-click to get to a needed button to perform a function.
The first thing LightSwitch does is move fast, very fast. Don’t take my word for it, see for yourself with the Live Demo: https://endtoendexample.lightswitchhelpwebsite.com/Client/default.htm (use your username and password from http://LightSwitchHelpWebsite.com).
So, from the beginning I need to remove anything that is not needed. If you want to search, click a button and do a search, otherwise hide all the search controls. When I do that 50% of all the items on the page go away!
When editing a help desk ticket, I have screen real estate used for things like the Description, Notes, Estimated Hours, Start and Compete boxes. The LightSwitch HTML Client is designed to allow a person to tap on an item and quickly edit it in a popup. Now I can eliminate the huge input boxes and show the data in much smaller text.
Don’t worry, every LightSwitch application does not have to look like a LightSwitch app. We can use JavaScript plug-ins to create rich user interfaces.
LightSwitch also has a built-in Knockout-like JavaScript data binding framework that allows the user interface to update without requiring the end-user to explicitly save the page.
You can also use Themeroller to help you create new themes because LightSwitch uses standard .css style sheets.
An application has a purpose, a task that the end-user needs to accomplish. We want to create applications that allow a user to accomplish that task as fast and as efficiently as possible. The LightSwitch HTML Client creates applications that do that.
SignalR is a fantastic tool for creating real-time applications. It is not just limited to making chat applications, but we will use a chat application to demonstrate how you can implement Forms Authentication with SignalR.
The reason you would want to use Forms Authentication, is that most websites use Forms Authentication now. Forms Authentication also works with all web browsers and most devices.
According to www.ASP.net:
SignalR is an open-source .NET library for building web applications that require live user interaction or real-time data updates. Examples include social applications, multiuser games, business collaboration, and news, weather, or financial update applications. These are often called real-time applications.
You can find more about SignalR at this link: http://signalr.net/.
When we run the LightBulbSignalRService project we see a simple SignalR application that uses Persistent Connections (rather than the Hubs that most examples use).
When the application starts, we are not logged in so our connection GUID shows with a message that we are connected.
If we click the Login as TestUser button, we are given a cookie and SignalR detects that we are authenticated.
SignalR also puts us in a group that has the same name as our user name.
We can open up another Tab, log in again as TestUser, enter a message and click the broadcast button…
When we return to the first Tab, it will see the Group Message because it is in the same group. This is just to demonstrate that you can authenticate a user and keep data in sync across multiple instances, and as will be demonstrated in the next step, multiple devices.
For example, imagine a user is working with your application on a web page and then wants to pick up from where they left off on their tablet or cell phone (or any other device).
Now, we run the LightBulbSignalRClient project and set the connection and click the Connect button.
We are now logged in via a Windows Forms Application using Forms Authentication!
We will first cover the Web Application (the LightBulbSignalRService project).
The first place we want to start is the web.config file. It is there that we want to turn Forms Authentication on by setting the authentication tag to mode=”Forms”:
xml version="1.0" encoding="UTF-8"?> <configuration> <system.web> <compilation debug="true" targetFramework="4.5" /> <httpRuntime targetFramework="4.5" /> <authentication mode="Forms"/> system.web> configuration>
xml version="1.0" encoding="UTF-8"?>
<configuration>
<system.web>
<compilation debug="true" targetFramework="4.5" />
<httpRuntime targetFramework="4.5" />
<authentication mode="Forms"/>
system.web>
configuration>
Next, we use NuGet to pull in SignalR.
We create a Global.asax file and add the following code to the Application_Start:
This simply indicates that we will have a SignalR Persistent Connection called MyConnection that can be accessed by the end point “echo”.
To implement the MyConnection class, we use the following code in the MyConnection.cs file:
using System.Collections.Generic; using System.Security.Principal; using System.Threading.Tasks; using System.Web; using Microsoft.AspNet.SignalR; using Microsoft.AspNet.SignalR.Hosting; public class MyConnection : PersistentConnection { protected override Task OnReceived(IRequest request, string connectionId, string data) { // Broadcast to all instances of the user // (that user is in a group that matches their user name) Groups.Send(getClientDescription(request, connectionId), "Group Message: " + data); // Broadcast to all clients return Connection.Broadcast("User: " + getClientDescription(request, connectionId) + " Sent: " + data); } protected override Task OnConnected(IRequest request, string connectionId) { // Add the user to a group that matches their user name // If they are not logged in, their group will be their // Connection ID Groups.Add(connectionId, getClientDescription(request, connectionId)); return Connection.Broadcast("User: " + getClientDescription(request, connectionId) + " connected"); } protected override Task OnReconnected(IRequest request, string connectionId) { // User Reconnected (they come back) return Connection.Broadcast("User: " + getClientDescription(request, connectionId) + " re-connected"); } protected override Task OnDisconnected(IRequest request, string connectionId) { // User Disconnected (they may come back) return Connection.Broadcast("User: " + getClientDescription(request, connectionId) + " disconnected"); } // Utility private static string getClientDescription(IRequest request, string connectionId) { // Note: Using HttpContext.Current is a no no with SignalR, // so get the IPrincipal from the SignalR IRequest via IRequest.User IPrincipal objIPrincipal = request.User; if (objIPrincipal != null) { // If user is authenticated get user name // otherwise use connectionId var name = objIPrincipal.Identity.IsAuthenticated ? objIPrincipal.Identity.Name : connectionId; return name; } else { return connectionId; } } }
using System.Collections.Generic;
using System.Security.Principal;
using System.Threading.Tasks;
using System.Web;
using Microsoft.AspNet.SignalR;
using Microsoft.AspNet.SignalR.Hosting;
public class MyConnection : PersistentConnection
{
protected override Task OnReceived(IRequest request, string connectionId, string data)
// Broadcast to all instances of the user
// (that user is in a group that matches their user name)
Groups.Send(getClientDescription(request, connectionId), "Group Message: " + data);
// Broadcast to all clients
return Connection.Broadcast("User: "
+ getClientDescription(request, connectionId) + " Sent: " + data);
}
protected override Task OnConnected(IRequest request, string connectionId)
// Add the user to a group that matches their user name
// If they are not logged in, their group will be their
// Connection ID
Groups.Add(connectionId, getClientDescription(request, connectionId));
+ getClientDescription(request, connectionId) + " connected");
protected override Task OnReconnected(IRequest request, string connectionId)
// User Reconnected (they come back)
+ getClientDescription(request, connectionId) + " re-connected");
protected override Task OnDisconnected(IRequest request, string connectionId)
// User Disconnected (they may come back)
+ getClientDescription(request, connectionId) + " disconnected");
// Utility
private static string getClientDescription(IRequest request, string connectionId)
// Note: Using HttpContext.Current is a no no with SignalR,
// so get the IPrincipal from the SignalR IRequest via IRequest.User
IPrincipal objIPrincipal = request.User;
if (objIPrincipal != null)
// If user is authenticated get user name
// otherwise use connectionId
var name = objIPrincipal.Identity.IsAuthenticated
? objIPrincipal.Identity.Name
: connectionId;
return name;
else
return connectionId;
Next, we create a Default.aspx page.
It uses the following markup:
<!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml"> <head runat="server"> <title>SignalR Testtitle> <script src="Scripts/jquery-1.9.1.min.js" type="text/javascript">script> <script src="Scripts/json2.js" type="text/javascript">script> <script src="Scripts/jquery.signalR-1.0.0.min.js" type="text/javascript">script> <script type="text/javascript"> $(function () { // For IIS deployment use: $.connection('echo'); var connection = $.connection('/echo'); // This is fired when a message is received connection.received(function (data) { $('#messages').append('<li>' + data + 'li>'); }); // Start the connection connection.start().done(function () { $("#broadcast").click(function () { connection.send($('#msg').val()); }); }); }); script> head> <body> <form id="form1" runat="server"> <div> <input type="text" id="msg" /> <input type="button" id="broadcast" value="broadcast" /> <asp:Button ID="btnLoginAsTest" runat="server" OnClick="btnLoginAsTest_Click" Text="Login as TestUser" /> <asp:Button ID="btnLogout" runat="server" OnClick="btnLogout_Click" Text="Logout" /> <ul id="messages"> ul> div> form> body> html>
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title>SignalR Testtitle>
<script src="Scripts/jquery-1.9.1.min.js" type="text/javascript">script>
<script src="Scripts/json2.js" type="text/javascript">script>
<script src="Scripts/jquery.signalR-1.0.0.min.js" type="text/javascript">script>
<script type="text/javascript">
$(function () {
// For IIS deployment use: $.connection('echo');
var connection = $.connection('/echo');
// This is fired when a message is received
connection.received(function (data) {
$('#messages').append('<li>' + data + 'li>');
});
// Start the connection
connection.start().done(function () {
$("#broadcast").click(function () {
connection.send($('#msg').val());
script>
head>
<body>
<form id="form1" runat="server">
<div>
<input type="text" id="msg" />
<input type="button" id="broadcast" value="broadcast" />
<asp:Button ID="btnLoginAsTest" runat="server"
OnClick="btnLoginAsTest_Click" Text="Login as TestUser" />
<asp:Button ID="btnLogout" runat="server"
OnClick="btnLogout_Click" Text="Logout" />
<ul id="messages">
ul>
div>
form>
body>
html>
And the following code behind:
using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Security; using System.Web.UI; using System.Web.UI.WebControls; namespace LightBulbSignalRService { public partial class Default : System.Web.UI.Page { protected void Page_Load(object sender, EventArgs e) { } #region btnLoginAsTest_Click protected void btnLoginAsTest_Click(object sender, EventArgs e) { // ************************* // Insert user validation here // For example, check the username and password in the database // ************************* // Log the user into the site FormsAuthenticationTicket ticket = new FormsAuthenticationTicket(1, "TestUser", DateTime.Now, DateTime.Now.AddDays(30), false, "SignalR", FormsAuthentication.FormsCookiePath); // Encrypt the ticket. string encTicket = FormsAuthentication.Encrypt(ticket); // Create the cookie. Response.Cookies.Add(new HttpCookie(FormsAuthentication.FormsCookieName, encTicket)); } #endregion #region btnLogout_Click protected void btnLogout_Click(object sender, EventArgs e) { FormsAuthentication.SignOut(); } #endregion } }
using System;
using System.Linq;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
namespace LightBulbSignalRService
public partial class Default : System.Web.UI.Page
protected void Page_Load(object sender, EventArgs e)
#region btnLoginAsTest_Click
protected void btnLoginAsTest_Click(object sender, EventArgs e)
// *************************
// Insert user validation here
// For example, check the username and password in the database
// Log the user into the site
FormsAuthenticationTicket ticket = new FormsAuthenticationTicket(1,
"TestUser",
DateTime.Now,
DateTime.Now.AddDays(30),
false,
"SignalR",
FormsAuthentication.FormsCookiePath);
// Encrypt the ticket.
string encTicket = FormsAuthentication.Encrypt(ticket);
// Create the cookie.
Response.Cookies.Add(new HttpCookie(FormsAuthentication.FormsCookieName, encTicket));
#endregion
#region btnLogout_Click
protected void btnLogout_Click(object sender, EventArgs e)
FormsAuthentication.SignOut();
To authenticate remote users, we create a file CreateCookie.aspx with the following code:
using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.UI; using System.Web.UI.WebControls; using System.Web.Security; namespace ODataSample { public partial class CreateCookie : System.Web.UI.Page { protected void Page_Load(object sender, EventArgs e) { string strUserName = Request.QueryString["UserName"]; string strPassword = Request.QueryString["Password"]; // ************************* // Insert user validation here // For example, check the username and password in the database // ************************* // Create cookie and return it FormsAuthenticationTicket ticket = new FormsAuthenticationTicket(1, strUserName, DateTime.Now, DateTime.Now.AddDays(30), false, "SignalR", FormsAuthentication.FormsCookiePath); // Encrypt the ticket. string encTicket = FormsAuthentication.Encrypt(ticket); // Create the cookie. Response.Write(encTicket); Response.End(); } } }
namespace ODataSample
public partial class CreateCookie : System.Web.UI.Page
string strUserName = Request.QueryString["UserName"];
string strPassword = Request.QueryString["Password"];
// Create cookie and return it
strUserName,
Response.Write(encTicket);
Response.End();
The Windows Forms Client has to have the ASP.NET SignalR Client installed.
We use NuGet to pull in the .NET SignalR Client.
We use the following code for the Connect button:
// Connect to the service connection = new Connection(txtSignalRWebsite.Text); // Fire connection_Received when message comes in connection.Received += connection_Received; // Create a Cookie Cookie objCookie = new Cookie(); // Set the values objCookie.Domain = txtDomain.Text; objCookie.Expires = DateTime.Now.AddMinutes(20); objCookie.HttpOnly = false; objCookie.Name = ".ASPXAUTH"; objCookie.Path = @"/"; objCookie.Secure = false; objCookie.Value = GetCookie(); // The Forms Auth Ticket // Create a Cookie container and put the Cookie inside connection.CookieContainer = new CookieContainer(); connection.CookieContainer.Add(objCookie); // Start the connection connection.Start().Wait();
// Connect to the service
connection = new Connection(txtSignalRWebsite.Text);
// Fire connection_Received when message comes in
connection.Received += connection_Received;
// Create a Cookie
Cookie objCookie = new Cookie();
// Set the values
objCookie.Domain = txtDomain.Text;
objCookie.Expires = DateTime.Now.AddMinutes(20);
objCookie.HttpOnly = false;
objCookie.Name = ".ASPXAUTH";
objCookie.Path = @"/";
objCookie.Secure = false;
objCookie.Value = GetCookie(); // The Forms Auth Ticket
// Create a Cookie container and put the Cookie inside
connection.CookieContainer = new CookieContainer();
connection.CookieContainer.Add(objCookie);
connection.Start().Wait();
The following is the code that connects to the CreateCookie.aspx file and gets the cookie:
private string GetCookie() { WebClient client = new WebClient(); // Add a user agent header in case the // requested URI contains a query. client.Headers.Add("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.2; .NET CLR 1.0.3705;)"); string strWebRequest = String.Format("{0}?UserName={1}&Password={2}", txtAuthWebsite.Text, txtUserName.Text, txtPassword.Text); Stream data = client.OpenRead(strWebRequest); StreamReader reader = new StreamReader(data); string strCookie = reader.ReadToEnd(); data.Close(); reader.Close(); return strCookie; }
private string GetCookie()
WebClient client = new WebClient();
// Add a user agent header in case the
// requested URI contains a query.
client.Headers.Add("user-agent",
"Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.2; .NET CLR 1.0.3705;)");
string strWebRequest =
String.Format("{0}?UserName={1}&Password={2}",
txtAuthWebsite.Text, txtUserName.Text, txtPassword.Text);
Stream data = client.OpenRead(strWebRequest);
StreamReader reader = new StreamReader(data);
string strCookie = reader.ReadToEnd();
data.Close();
reader.Close();
return strCookie;
This method was created because this is what it appears that one of the SignalR creators, David Fowler indicated was the way it should be handled (http://stackoverflow.com/questions/11488461/forms-authentication-with-signalr).
You can download the code from this link:
http://silverlight.adefwebserver.com/Files/SignalrRWindowsFormsAuth.zip
My 8 year old cousin just completed his first Android game today. It is called Pet Brawl. He actually wrote it himself. I have been tutoring him for 1 1/2 hours each month for over a year, but he wrote all the code himself.
Originally I started working with him after he had completed a computer camp where they taught him how to use the computer programming application called Scratch. It is a great program and I recommend it highly as an entry level programming language for all ages.
Our sessions would start with a task I would assign him at the end of the previous session. Things like “make the cat pick up four objects in order”. They were designed to be too hard and when we got together I would explain how to solve the problem.
Next we moved to App Inventor. Another programming language and application builder created by some of the same people that created Scratch. The nice thing about App Inventor is that you can run the programs you create on your Android device.
This time I set an ambitious goal. He would complete an entire program and we would distribute it. Originally he wanted the game to have you to pick a “pet” and then you would “brawl” against the computer.
First we started by learning how to move the character around the screen (I had to figure it out myself and I led the Google session, and after finding an example I would provide guidance and encouragement and he worked it out). I only touched the keyboard less than 2 minutes a session (usually to find the element I wanted him to try next).
As time went on he scaled the game back a bit (he actually wanted to finish it), so the final game just has you trying to survive (don’t let the Phoenix touch you, the Dragon) until the clock runs down.
The game is actually quite challenging. He programmed it himself, and he is only eight years old.
The picture above is Carter showing his father the game he just created running on his father’s Galaxy Note.
You can download the .apk at the link below. It will run on most any Android device.
Download: PetBrall.apk