June 2012

Volume 27 Number 06

Leading Lightswitch - Tales of Advanced LightSwitch Client Customizations

By Jan Van der Haegen | June 2012

One of the biggest advantages LightSwitch has over conventional RAD environments I have experimented with is that the generated application isn’t a closed box. Using a virtually unlimited number of extension points, you can take control of almost any element you want. After a year of working with LightSwitch, I still haven’t hit any brick walls, that is, challenges I couldn’t surmount because of limitations imposed by LightSwitch.

For example, in the first Leading LightSwitch article I wrote, I completely replaced the LightSwitch client with a Windows Phone 7 client. After looking at the LightSwitch metadata-driven MVVM model, I wrapped the outer tier with an ASP.NET Web site to allow users to log in with their social media credentials.

Before diving in to the LightSwitch architecture and playing with the server implementation, which I’ll do over the next couple months, I wanted to bring you one more Leading LightSwitch article about advanced client-side customizations you might not be aware of. In this “micro-novel,” you’ll find four tales that prove to me that with LightSwitch, if you can imagine something, you’re just minutes away from creating it.

The Mega-Filter

I’ll be honest: my SQL Server skills are currently a bit rusty. Who could blame me for turning to LightSwitch given its visually stunning query designer that accommodates all my query needs? Bob, the IT manager for a large retailer with chains of stores in numerous countries, requested software to help him manage the backend of the company’s business. Specifically, he wanted a screen that would let him browse work tickets from various angles. 

Here’s what Bob told me: “A work ticket comes from a particular shop that belongs to a particular chain, located in a particular city or region. It is made for a particular customer, by a particular employee, on a particular date and time, and it contains sales records of particular articles, paid with particular payment types. Only rarely are our employees interested in querying the tickets solely based on properties such as start date or total value. But we have use cases for filtering the tickets by each category I just mentioned.” 

Stories like this usually mean the customer doesn’t really have a particular use case in mind but still wants an application that does whatever he wants, whenever he wants it. Given that the customer is always right, I started thinking about a mega-query that could address Bob’s needs.

I can create an extremely large query within the LightSwitch query designer without much effort, but I oversimplified the required query because a more realistic version of it wouldn’t fit on a single screen, or in Figure 1, and I wouldn’t be able to show you the entire query.

An advanced LightSwitch query
Figure 1 An advanced LightSwitch query

Figure 2shows a screen generated from the query in Figure 1. In the command bar, I clicked Add Screen and then selected a new Search Data screen. By default, LightSwitch creates a screen and opens it in the screen designer.

A screen generated on the query in Figure 1
Figure 2 A screen generated on the query in Figure 1

At run time, Bob can now search tickets on any combination of the optional parameters shown in Figure 3: ID, Shop, Customer, Value (minimum or maximum), Started time and Finished time.

Search screen with filter criteria
Figure 3 Search screen with filter criteria

Bob probably hasn’t memorized the ID of each shop, so that search field isn’t very useful as is. He needs to be able to select a shop by name instead of having to enter the shopId manually.

Like all things LightSwitch, we’re just a couple clicks away from modifying this application to give Bob what he wants. In the screen designer, I clicked the Add Data Item button in the command bar, and in the Add Data Item dialog box, I added a new Local Property of the type Shop (Entity), as shown in Figure 4.

Add Data Item dialog box
Figure 4 Add Data Item dialog box

From the left-hand side of the screen designer, I dragged this Shop(Entity) onto the screen in the center and removed the Ticket Shop Id control since it’s no longer needed. I can change the control that’s being used to an Auto Complete Box or a Modal Window Picker, whatever fits in a particular solution. Figure 5 shows the result of these modifications to the visual tree.

Rebinding the query argument in the screen designer
Figure 5 Rebinding the query argument in the screen designer

That takes care of the controls, but in the ViewModel (at the left-hand side of the screen designer), notice that the query parameter ShopId is still bound to the local screen property TicketShopId, which no longer appears on the screen. To correct this, I selected ShopId and in the properties panel, changed the value of Parameter Binding to Shop.Id, as in Figure 5. When I next launch the application, the filter criteria section of the screen now has a Shop Modal Window Picker (or an Auto Complete Box if I had wanted one) where the Ticket Shop Id text box once was, as shown in Figure 6.

Improved search screen
Figure 6 Improved search screen

When the ... (ellipses) at the right of the Shop box is clicked, the modal window Select Shop shown in Figure 7 opens. From there, Bob and his employees can select the particular shop they want to review.

Select Shop modal window
Figure 7 Select Shop modal window

Bob wants to have every possible use case he can think of implemented for this one screen (not something I recommend because creating a screen around each separate business process or use case is more effective). That’s what Bob wanted, however, so that he can rinse and repeat this process for such parameters as employee, chain, region, payment, customer, workstation, article, classification and possible other categories that might come up.

Where’s the Metro?

I love it when my clients are a bit IT-minded. However, during an initial talk with Hank, who asked me to create a small CRM (customer relationship management) application for him, I found out that there’s a big difference between being IT-minded and having some IT buzzwords in your mind.

Three days after the first phone call, Hank and I sat down to discuss some details of the application he wanted, and I showed him a proof of concept I had put together the night before. Hank was wildly enthusiastic about what I had prepared, and talking about his needs in more detail was helpful for both of us. During this initial talk with a client, I often take advantage of the built-in LightSwitch run-time screen editor to tailor the application as we’re discussing it.

Hank and I chatted for hours, and my respect for him grew as we spoke. He was right there with me as we considered how to make this application fit his business processes. Unfortunately, near the end of our meeting, Hank said, “So far the app looks great—but where’s the Metro?” I stared at him, part of me wondering if he was asking about the subway system running through the city or if he wanted an extra module that shows live subway information. Then I noticed that his laptop was open to the Windows 8 start page. I said, “If you’re referring to the Metro design guidelines, the Cosmopolitan theme we’re using adheres to those.” Hank replied, “But there are no squares.”

I knew he was referring to the tiles found in many Metro applications, so I started creating a new screen. Not all screens in LightSwitch have to be based on data. For example, to create the new List and Details screen in Figure 8, I didn’t even have to select anything in the Screen Data combo box.

A screen created without screen data
Figure 8 A screen created without screen data

After I clicked OK in the Add New Screen dialog box, LightSwitch generated an empty screen and opened it in the screen designer. Then from the command bar, I clicked the Add Data Item button and in the Add Data Item dialog box, added a new method named OpenPeopleScreen, as shown in Figure 9.

Adding a new method as a data item to the screen
Figure 9 Adding a new method as a data item to the screen

I edited the code for this new method so that it opened a particular screen in my application by right-clicking on it in the screen designer and selecting Edit Execute Code. I then replaced the code with the following:

namespace LightSwitchApplication
{
    public partial class DashBoard
    {
        partial void OpenPeopleScreen_Execute()
        {
            // LightSwitch generates this method if you create a screen
            // called CustomersListDetail
            this.Application.ShowCustomersListDetail();
        }
    }
}

Next I dragged the method in the screen designer onto the screen, changed the control from a button to a link, added a static image, and with the help of some layout controls, created the screen in Figure 10.

A really small dashboard screen in the screen designer
Figure 10 A really small dashboard screen in the screen designer

After making the preceding adjustments, I fired up the application and showed Hank how easy it was to add some squares to his application, as in Figure 11.

The dashboard screen at run time
Figure 11 The dashboard screen at run time

But the conversation went downhill from there. When I phoned Hank the next day, he told me the deal was off, saying, “This application has to be a state-of-the-art, gigantic and completely modern CRM. How can I trust a developer who doesn’t even know what Metro is?”

I didn’t argue with Hank. In hindsight, I’m relieved I didn’t commit to this project. What Hank, with his smattering of knowledge about Metro, didn’t understand is that the presence or absence of tiles isn’t what makes an application adhere to or violate the Metro design guidelines—although I must admit the “squares” do provide a beautiful, modern finish for the new LightSwitch default look and feel (the Cosmopolitan theme), as you can see in Figure 12.

Finished version of a dashboard screen
Figure 12 Finished version of a dashboard screen

What a Default Customer Looks Like

Of all my customers, I like working with Nancy the most. She runs a small but lucrative online shop from a small office downtown, where she works with her husband and five employees. Nancy wants everything in her company to work smoothly and to be optimized for her business processes, so she is closely involved in the application development process. She is the opposite of Hank—that is, she doesn’t pretend to be an IT expert. Yet when I showed her how a LightSwitch app works, she immediately grasped how an OData-based backend would help align all the applications she uses.

Over the six weeks I worked with Nancy and her team, I spent many days coding on-site so I could question her and her staff when I needed to. I also wanted Nancy to learn enough about LightSwitch that she could modify the app herself in the future.

One day, Jean-Paul passed by while Nancy and I were working on the main customer screen. Besides being young, “fashion-aware” (his own words) and unafraid of saying something the second it crosses his mind, Jean-Paul will be one of the principal users of the application. Because Nancy’s company is so small, Jean-Paul is not only in charge of CRM, but also the head of shipping and human resources as well as the financial department. He stopped and said, “Nice to see you have found out what a default customer looks like.”

Let me explain the context for Jean-Paul’s comment. When I first presented a proof of concept for the application, Jean-Paul immediately asked me to change the customer List and Detail screen since the look and feel offended his visual sensibilities. By default, the list contains the summary property of the entity, which results in the screen shown in Figure 13.

A default List and Details screen
Figure 13 A default List and Details screen

 

I explained to Jean-Paul that at design time (or using the runtime screen editor), you can easily change the look of this “item template.” Jean-Paul was much happier when I changed the list part of the screen to look like Figure 14.

Modified list from Figure 13
Figure 14 Modified list from Figure 13

I altered the list part of the original screen in the screen designer by changing the control in the List from a Summary control (label) to a Picture and Text control. Figure 15shows where I made these modifications.

Modifying through the screen designer
Figure 15 Modifying through the screen designer

Nancy’s customers get “store credit points” if they complete an online profile. Not all her customers choose to upload a picture of themselves, so a lot of blanks are left in the list on the screen—a flaw I discovered in my test data when I realized I didn’t have any pictures of my cats Muppet and Munchkin. Jean-Paul asked me to add a default image in these cases so that the screen wouldn’t look so empty.

LightSwitch doesn’t support a picture control with a default image, but as I wrote in this article’s introduction, if you can imagine it, you’re minutes away from creating it. The trick here is to create a simple Silverlight user control. I just added a new Silverlight class library to my solution and then added a new user control named CustomImageControl to it. From the screen designer, I then replaced the default Image Viewer with Custom Control, as shown in Figure 16.

Changing from the Image Viewer to Custom Control in the LightSwitch screen designer
Figure 16 Changing from the Image Viewer to Custom Control in the LightSwitch screen designer

When I clicked Custom Control in the screen designer, LightSwitch started a wizard I used to sequentially add a reference to the Silverlight class library and to select the CustomImageControl as the control to use. To make this control work, however, I needed to know that when I create a custom control, LightSwitch binds an IContentItem as the datacontext. This interface belongs to the ViewModelMetaData layer I talked about in my April 2012 Leading LightSwitch article.

This interface has a property named Screen that is a reference to the IScreenObject, also known as the ViewModel of the screen, visible on the left side of the screen designer, and a property named Value. Obviously, in this case I wanted to bind directly to that Value property from the XAML, as shown in Figure 17.

<UserControl x:Class="CustomControls.CustomImageControl"
    xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="https://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="https://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:local="clr-namespace:CustomControls"
    mc:Ignorable="d"
    d:DesignHeight="300" d:DesignWidth="400">
    <UserControl.Resources>
        <ResourceDictionary>
            <local:ImageToVisibleConverter 
                x:Name="ImageToVisibleConverter" />
            <local:ImageToCollapsedConverter 
                x:Name="ImageToCollapsedConverter" />
            <local:LightSwitchImageToSilverLightImageSourceConverter
                x:Name="LSImageToSLImageSourceConverter" />
        </ResourceDictionary>
    </UserControl.Resources>
    <Grid x:Name="LayoutRoot"Background="White">
        <Image Source="Customer.png"
               Visibility="{Binding Value,
                  Converter={StaticResource ImageToCollapsedConverter}}"
               Stretch="UniformToFill"
               Height="100" Width="100"/>
        <Image Source="{Binding Value,
             Converter={StaticResource LSImageToSLImageSourceConverter}}"
             Visibility="{Binding Value,
             Converter={StaticResource ImageToVisibleConverter}}"
              Stretch="UniformToFill"
              Height="100" Width="100"/>
    </Grid>
</UserControl>

Figure 17 XAML for the CustomImageControl

Because in LightSwitch an image is kept in memory as an array of bytes, I needed to convert this array to Visible, Collapsed or a MediaSource, depending on the type of property I wanted to bind to the image. This heavy lifting is done in IValueConverter implementations, as shown in Figure 18.

using System;
using System.IO;
using System.Windows.Data;
using System.Windows.Media.Imaging;
using Microsoft.LightSwitch.Presentation;
namespace CustomControls
{
    public class LightSwitchImageToSilverLightImageSourceConverter : IValueConverter
    {
        public object Convert(object value, Type targetType, 
            object parameter, System.Globalization.CultureInfo culture)
        {
            var contentItem = value as IContentItem;
            var theValue = 
                contentItem == null ? value : contentItem.Value;
            byte[] byteArray = theValue  as byte[];
            var bitMapImage = new BitmapImage();
            if (byteArray != null && byteArray.Length > 0)
            {
                MemoryStream stream = new MemoryStream(byteArray);
                bitMapImage.SetSource(stream);
            }
            return bitMapImage;
        }
        public object ConvertBack(object value, Type targetType, 
            object parameter, System.Globalization.CultureInfo culture)
        {
            throw new NotImplementedException();
        }
    }
}

Figure 18 IValueConverter implementation that converts a byte[] into a BitmapImage

There is no code behind since LightSwitch binds the IContentItem as the DataContext, which means I can do everything from XAML with a little help from the converters. So the XAML is straightforward.

There is a grid with two Image controls. If the customer has uploaded a picture, that picture will be bound to the second Image control—the first one will be hidden. If no picture has been uploaded, only the first Image control, the one showing a default picture, will be visible, as illustrated in Figure 19.

Control that doesn’t show blanks
Figure 19 Control that doesn’t show blanks

When Jean-Paul saw the finished screen, he joked, “I didn’t realize our default customer is a tie-wearing, round-headed bald guy.” Even Matthew had to laugh.

Turning Cosmopolitan Upside Down

Matthew is Nancy’s husband and partner in the company. The first time I met Nancy and Matthew, Nancy welcomed me and we chatted about her company’s software needs and how I could help her. Before Nancy introduced me to the rest of her crew, who would all attend the demo of my proof of concept, she warned me about Matthew. “He’s such a sweet and funny man, but you’ll need lots of icebreaking before he’ll warm up to you. Do you know any good jokes?”

She was right about Matthew. I sensed that uncomfortable “I don’t know you so I don’t trust you” feeling from him throughout the demo. He asked me one difficult question after another. Near the end of the demo, he seemed to be the only person who wasn’t completely sold. His last remark to me was, “Not as bad as I thought, but I need the icons on top of the window, not in that banner at the bottom.”

I could understand his comment. Before the Metro hype hit, Microsoft was implementing ribbons in every piece of software it could. Ribbons are traditionally located at the top of the screen, and some people don’t think they belong anywhere else. I already had experience creating a shell extension on-site in around 15 minutes, so I replied to Matthew with, “I’ll take that challenge, sir.” He objected, saying not to bother, but before he had a chance to stop me, I was adding a custom shell to the solution.

A lot of people who use LightSwitch stay away from creating custom shells or themes even though it isn’t that difficult. One perceived obstacle is that custom shells and themes can be developed only in a LightSwitch Extensions project, not in the LightSwitch application. LightSwitch Extensions projects can be really annoying to debug. Because several of my customers have wanted a shell, or at least a theme tailored to their trademark, I created an extension that removes this restriction: ExtensionsMadeEasy. 

With ExtensionsMadeEasy, I can create shells and themes directly from the LightSwitch client, which is much easier to develop and debug than an extension project. I can still create the shell or theme in a separate Silverlight class library and compile it into a reusable LightSwitch extension later. Here’s how it works: First download, install and activate ExtensionsMadeEasy in the LightSwitch project. Next, from Solution Explorer, change the LightSwitch application from Logical View to File View. Right-click on the client project, and select Add New Item. A shell is nothing more than a very complex LightSwitch user control, so just add a new user control named MyShell, as demonstrated in Figure 20.

Adding a new shell directly to a LightSwitch client project
Figure 20 Adding a new shell directly to a LightSwitch client project

The XAML of the user code can be replaced with anything you want, but since I was coding on-site with Nancy, Matthew and their five employees watching me, I reused the Cosmopolitan shell in my control and simply applied a PlaneProjection to it, as shown in Figure 21.

<UserControl x:Class="LightSwitchApplication.MyShell"
    xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="https://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="https://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:cosmo="clr-namespace:Microsoft.LightSwitch.Cosmopolitan.Presentation.Shells;assembly=Microsoft.LightSwitch.Cosmopolitan.Client"  
    mc:Ignorable="d"
    d:DesignHeight="300" d:DesignWidth="400">
    <UserControl.Projection>
        <PlaneProjection RotationX="180" />
    </UserControl.Projection>
    <cosmo:CosmopolitanShell />
</UserControl>

Figure 21 XAML that defines the visual part of the shell

From the LightSwitch application designer, I changed the shell used from the LightSwitch Cosmopolitan Shell to EasyShell, as shown in Figure 22.

Using EasyShell to bridge LightSwitch and a user control
Figure 22 Using EasyShell to bridge LightSwitch and a user control

EasyShell comes with ExtensionsMadeEasy and does all the hard work, such as integrating with the LightSwitch IDE. At run time, EasyShell tries to discover the custom user control and use that as the shell.

My final task was to make the user control discoverable by EasyShell. I just added a small class to the LightSwitch client project. You can’t do this from a separate class library—you have to add the class to the LightSwitch client project because of the way a LightSwitch client uses Managed Extensibility Framework (MEF) to discover all the different parts. To save time, I just appended the class to the code-behind file of the user control, as shown in Figure 23.

using System;
using System.Windows.Controls;
using Microsoft.LightSwitch.Runtime.Shell;
namespace LightSwitchApplication
{
    public partial class MyShell : UserControl
    {
        public MyShell()
        {
            InitializeComponent();
        }
    }
    public class MyShellExporter : ExtensionsMadeEasy.ClientAPI.Shell.EasyShellExporter
    <MyShellExporter>, IShell
    {
        public string Name
        {
            get { return "MyShell"; }
        }
        public Uri ShellUri
        {
            get
            {
                var uri = new Uri(
                    string.Format(@"/{0};component/MyShell.xaml",
                    this.GetType().Assembly.FullName.Split(',')[0])
                    , UriKind.Relative);
                return uri;
            }
        }
    }
}

Figure 23 Code behind the user control with an added Exporter class

When the LightSwitch application is launched, LightSwitch uses EasyShell as the shell implementation. The shell implementation in turn uses MEF to discover an IShellExporter, which is implemented by the EasyShellExporter base class. This exporter class exports the URI of the actual user control (MyShell.xaml) to EasyShell.

If you’re unfamiliar with PlaneProjections, the result might not be what you expect, as you can see in Figure 24.

User control mirroring the Cosmopolitan shell
Figure 24 User control mirroring the Cosmopolitan shell

I typed Annys in the search control and executed the query. The shell might be mirrored across two axes, but it’s still usable and responsive. Once the data was on the screen, I looked Matthew straight in the eye and, with my best poker face, told him, “Here you go; icons are back on top.”

He stared at me in disbelief. And then I noticed him cracking a small smile. That smile turned into laughter. He got up, shook my hand, and said, “Welcome to the family, son. If you can pull this off, you can make anything work.”

Credit Where Credit Is Due

I want to thank Syncfusion for their free Metro Studio, in which I created each of the icons in this article’s screenshots.

The four tales in this article are based on actual technical details, but names have been changed throughout to protect the innocent.

Tale 1, the mega-filter, is a technique Beth Massimailed me when she was reviewing my eBook. Although it was too late in the process to update the book with a completely new chapter that would include this tip, I didn’t want to keep it to myself either.

Tale 2, where’s the metro, is based on one of Paul Patterson’s latest blog posts: Microsoft LightSwitch - Simply Beautiful Applications. Figure 12, featured on his site, is used in this article with his permission.

Tale 3, what a default customer looks like, is based on actual experiences. Nancy, Matthew and Jean-Paul (not their real names), you are the best clients anyone could wish for. 

Tale 4, turning Cosmopolitan upside down, was good for laugh but has a few flaws other than the obvious. For example, a lot of the elements in a LightSwitch application (e.g., modal windows, validation tooltips and drop-down menus) are not under the shell’s control and thus are not subjected to the PlaneProjection. If you want to read more about modal windows, I have an extra tale about Nancy on my blog that explains how to create reusable modal windows in the LightSwitch screen designer.


Jan Van der Haegen* *is a green geek who turns coffee into software. He’s a loving husband, he’s proud to be part of an international team at Centric Belgium, and he’s so addicted to learning about any .NET technology – Visual Studio LightSwitch in particular – that he maintains a blog on his coding experiments.

Thanks to the following technical expert for reviewing this article: community rock star Michael Washington (https://www.lightswitchhelpwebsite.com).