Pixata Custom Controls
For Lightswitch

Recent Posts

Popular tags (# posts in brackets)

Anonymous types (3) ASP.NET (5) C# (3) C# tricks and tips (2) Computers (6) Design patterns (3) DomainDataSource (3) Dynamic data (4) Entity framework (3) Entity model framework (5) F# (3) LightSwitch (12) Linq (6) Microsoft (2) MVP (2) MVVM (2) Project Euler (2) RIA services (5) Silverlight (2) SQL Server (4) Unit testing (4) Visual Studio (7) WCF (3) WPF (3)

Gratuitous link to StackExchange

Archives


Categories


Disclaimer

The opinions expressed herein are my own personal opinions and do not represent my employer's view in any way.

Actually, as I'm self-employed, I guess that means that any views I expressed here aren't my own. That's confusing!


Acknowledgments

Theme modified from one by Tom Watts
C#/F# code styling by Manoli (for posts pre-2016) and Google code prettify (for post from Jan 2016 and beyond)


My rambling thoughts on exploring the .NET framework and related technologies

As my avid reader will know, I’m a huge fan of Visual Studio Lightswitch, mainly for the ease and speed with which you can get an impressive CRUD (forms over data) application up and running. However, one of the (admittedly few) limitations that I’ve found is that you don’t have as much control over the user interface (UI) as you do in WPF, Silverlight or WinForms applications. Whilst this is generally not a problem, it can sometimes be a issue.

For example, I’m currently working on an application which generates emails and documents at various times. These are based on templates that the user can modify. In order to make life easy for them, I provided an editor in the application, and wanted to give them an easy way of inserting placeholder text that would be replaced when the email or document was generated. In order to add buttons for the placeholder text items, I added several methods to the screen, and dragged these to the designer to create the buttons. As they can modify document templates and email templates, and the latter have both a subject and body, I needed three buttons for every placeholder text in use. The email tab of this screen looked like this...

The Lightswitch application before the custom toolbar

 

As you can see, I placed the two sets of buttons on the right, so they would be easily accessible when typing the template.

The problem with this is that those buttons don’t look very pretty, and they take up quite a bit of screen space. The Documents tab has another set of buttons, for inserting text into the document body. Apart from the looks, this resulted in three almost identical sets of six almost identical screen methods, with the associated problems in maintenance. This bothered me enough to wonder how hard it would be to implement a custom toolbar for Lightswitch, that could be used three times, saving me all that almost identical code.

This turned out to be almost embarrassingly easy, as I will explain with a simple example.

A Simple Example

If you want to follow along, create a new Lightswitch application, and add a table called Customers. Actually, you can add anything you like, this was just the first (moderately sensible) thing that came into my head. The table looks like this...

The Customers table in the Lightswitch designer

Now create a “List and Details Screen” based on this table. At this stage, you can run the application and add some data - don’tcha just LURVE Lightswitch!

In a desperate attempt to think of something useful here, I decided that I wanted a quick way to increase the number of orders recorded for the customer (I know, this would normally come from somewhere else in the database, but bear with me, the point here is to show you how to make a toolbar, not how to write a sensible application!) and to update the date of last contact to the current date and time.

I could do this by adding two methods to the screen, and dragging them onto the designer, but this would look as ugly as the sample I showed above. We want our users to we wowed by the fab UI we gave them, so we’re going to add... elephants! Huh? Bear with me folks, it will all become clear.

To create the toolbar, switch to File View in the Solution Explorer, and find the Client project. Add a folder named UserControls to this project, and add a Silverlight User Control named CustomerToolbar.xaml to the folder. Your Solution Explorer should look something like this...

The solution explorer in Visual Studio when you've added the user control

Aside: Strictly speaking, you don’t need to add the folder, but it’s a good idea to help keep things separate. It’s also worth noting that you don’t need to put your user control in a separate project as many people claim. Unless you’re writing something that will be reused a lot, in which case you’re better of writing an extension, there’s no need to bother with a separate project. As far as I know, the way I have shown will also work with the Express version of Visual Studio, which doesn’t allow extra projects to be added.

Now double-click the CustomerToolbar.xaml file to open it up in the Silverlight designer. There are quite a few ways of creating a toolbar, and I chose to use a set of Button controls inside a StackPanel. My XAML looked like this...

<UserControl x:Class="LightSwitchApplication.UserControls.CustomerToolbar"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
             mc:Ignorable="d"
             Height="40"
             Width="130">
 
  <StackPanel Orientation="Horizontal">
    <Button Height="40"
            Width="40"
            Margin="0"
            Name="BtnElephant"
            Click="BtnElephant_Click">
      <Image Source="/CustomToolbar.Client;component/Images/elephant.png"
             Height="32"
             Width="32" />
    </Button>
    <Button Height="40"
            Width="40"
            Margin="5,0"
            Name="BtnCoffee"
            Click="BtnCoffee_Click">
      <Image Source="/CustomToolbar.Client;component/Images/rss.png"
             Height="32"
             Width="32" />
    </Button>
    <Button Height="40"
            Width="40"
            Name="BtnBell"
            Click="BtnBell_Click">
      <Image Source="/CustomToolbar.Client;component/Images/alert.png"
             Height="32"
             Width="32" />
    </Button>
  </StackPanel>
</UserControl>

I took the easy approach to adding the images, by creating empty <Image> tags, then clicking the Source property in the Properties panel, clicking the Add button and navigating my way to the PNG file I was using. Visual Studio added the image to my project and set the Build Action to Resource for me. You can do this manually, but why bother?

The designer now looked like this...

The toolbar control in the Visual Studio designer

 

As you can see, we have an elephant, a coffee cup and a bell. All the icons came from www.findicons.com, my usual favourite source of free icons. I picked these three more for their looks than their significance to the task at hand. In a real application, you would want to pick something more meaningful. I also didn’t add tool tips in the XAML above. These are a good idea, and will be added in the final version shown later.

Now switch the Solution Explorer back to Logical View, and double-click the Customers screen you created earlier. We want to add the toolbar to our screen, but as you probably know, version 1 of Lightswitch only allows you to add data-bound items to a screen. We get around this by adding a Local Property to the screen. Click the “Add Data Item…” button at the top of the window, then choose “Local Property,” uncheck the “Is Required” checkbox and give it a name. I tend to use StaticProperty, as this helps me remember what it is, but you can call it anything you like.

Now drag the property onto your screen wherever you want the toolbar to appear. In my case, the screen is a “List and Details” screen, and I want the toolbar above the details section. Once it’s on the screen, change the control type to Custom Control, and in the Properties panel, click the “Change” link. From the window that appears, choose the control...

The window where you can pick the custom control

My screen designer now looked like this...

The Lightswitch screen designer when the custom control has been added

If you run the application now, you’ll see the toolbar looking very smart...

The toolbar on the Lightswitch application

Looks very nice, but it doesn’t to anything yet. We’ll fix that right now.

In order to have the toolbar notify the Lightswitch screen when someone clicks a button, we’ll add some event handlers to the code. Switch to the code-behind file for the control, which in my case is called CustomerToolbar.xaml.cs (you’ll need to be in File View to see this), and change the code to look like this...

using System;
using System.Windows;
using System.Windows.Controls;
 
namespace LightSwitchApplication.UserControls {
  public partial class CustomerToolbar : UserControl {
    public CustomerToolbar() {
      InitializeComponent();
    }
 
    public event EventHandler Elephant;
 
    private void BtnElephant_Click(object sender, RoutedEventArgs e) {
      EventHandler eh = Elephant;
      if (eh != null) {
        eh(this, null);
      }
    }
 
    public event EventHandler Coffee;
 
    private void BtnCoffee_Click(object sender, RoutedEventArgs e) {
      EventHandler eh = Coffee;
      if (eh != null) {
        eh(this, null);
      }
    }
 
    public event EventHandler Bell;
 
    private void BtnBell_Click(object sender, RoutedEventArgs e) {
      EventHandler eh = Bell;
      if (eh != null) {
        eh(this, null);
      }
    }
  }
}

In this case, I’ve added three separate event handlers, one for each button. This is fine if each button does something quite different. In the case of my original application, all the buttons are to do a very similar task, so we can make the code easier, as I’ll show soon. Also, you would probably want to refactor this code to make it slimmer, as there is a lot of almost identical code here. I didn’t bother for this simple example, but will show how this could be done later.

Back in the Lightswitch screen code behind, we simply need to catch the toolbar’s events, and then do whatever we want in response. For the purposes of this demo, I’m going to have the elephant button increase the number of orders for the current customer, and the coffee button set the date last contacted to the current date/time. Here is the complete screen code...

using System;
using System.Collections.Generic;
using LightSwitchApplication.UserControls;
using Microsoft.LightSwitch;
using Microsoft.LightSwitch.Presentation.Extensions;
 
namespace LightSwitchApplication {
  public partial class CustomersScreen {
    partial void CustomersScreen_InitializeDataWorkspace(List<IDataService> saveChangesTo) {
      this.FindControl("Toolbar").ControlAvailable += (S, E) => {
        ((CustomerToolbar)E.Control).Elephant += new EventHandler(CustomersScreen_Elephant);
        ((CustomerToolbar)E.Control).Coffee += new EventHandler(CustomersScreen_Coffee);
        ((CustomerToolbar)E.Control).Bell += new EventHandler(CustomersScreen_Bell);
      };
    }
 
    void CustomersScreen_Elephant(object sender, EventArgs e) {
      if (CustomersSet.SelectedItem != null) {
        CustomersSet.SelectedItem.NumberOfOrders++;
      }
    }
 
    void CustomersScreen_Coffee(object sender, EventArgs e) {
      if (CustomersSet.SelectedItem != null) {
        CustomersSet.SelectedItem.DateOfLastContact = DateTime.Now;
      }
    }
 
    void CustomersScreen_Bell(object sender, EventArgs e) {
    }
  }
}

As you can see, in the InitializeDataWorkspace event, we grab the toolbar control and set up handlers for the three events. In the event handlers themselves, we set the customer’s properties. I haven’t done anything in the third event handler here, mainly because I couldn’t think of anything useful, given the limited scope of this simple example!

If you run the application now, you’ll find that the elephant and coffee cup buttons work as expected (assuming you expect an elephant to increase the number of orders, and a coffee cup to set today’s date of course!). We have a fully working toolbar, with amazingly little effort.

In this sample, I have added a separate event handler for each button, which is fine when the buttons do quite distinct tasks, as these do. In the original application I showed at the top, the buttons all do a very similar job, so we can improve this control quite a lot...

Tidying Up The Toolbar Code

As I mentioned above, my original case was where I wanted the buttons to insert standard bits of text into controls on the screen. As each button on the toolbar is to do basically the same job, with only a variation in the actual text, we can set up one event in the toolbar control, and use an EventArgs class to pass the text back to the Lightswitch screen.

Imagine the requirements for our sample app have changed, and we now want to set the Customer Name to one of three specified bits of text, depending on which button is pressed.

Switch to File View again, and in the same folder where you created the user control, add a class called CustomerToolbarEventArgs, whose code should look like this...

using System;
 
namespace LightSwitchApplication.UserControls {
  public class CustomerToolbarEventArgs : EventArgs {
    public string Text { get; set; }
  }
}

This class adds a single string property to the standard EventArgs class, which we’ll use to pass the text to be inserted back to the Lightswitch screen.

Change the toolbar control’s code to look like this...

using System;
using System.Windows;
using System.Windows.Controls;
 
namespace LightSwitchApplication.UserControls {
  public partial class CustomerToolbar : UserControl {
    public CustomerToolbar() {
      InitializeComponent();
    }
 
    public event EventHandler<CustomerToolbarEventArgs> BtnClick;
 
    private void FireEvent(string Text) {
      CustomerToolbarEventArgs Ev = new CustomerToolbarEventArgs {
        Text = Text
      };
      EventHandler<CustomerToolbarEventArgs> eh = BtnClick;
      if (eh != null) {
        eh(this, Ev);
      }
    }
 
    private void BtnElephant_Click(object sender, RoutedEventArgs e) {
      FireEvent("I like buns!");
    }
 
    private void BtnCoffee_Click(object sender, RoutedEventArgs e) {
      FireEvent("No sugar please!");
    }
 
    private void BtnBell_Click(object sender, RoutedEventArgs e) {
      FireEvent("Ding dong!");
    }
  }
}

As you can see, we now only have one event handler, which is generic to the CustomerToolbarEventArgs that we created above. The Click event handlers for the three buttons call a common method that sets up an instance of this class, and raises the event.

Now, we can simplify the Lightswitch screen by changing the code to this...

using System;
using System.Collections.Generic;
using LightSwitchApplication.UserControls;
using Microsoft.LightSwitch;
using Microsoft.LightSwitch.Presentation.Extensions;
 
namespace LightSwitchApplication {
  public partial class CustomersScreen {
    partial void CustomersScreen_InitializeDataWorkspace(List<IDataService> saveChangesTo) {
      this.FindControl("Toolbar").ControlAvailable += (S, E) => {
        ((CustomerToolbar)E.Control).BtnClick += new EventHandler<CustomerToolbarEventArgs>(CustomersScreen_ToolbarButtonClick);
      };
    }
 
    void CustomersScreen_ToolbarButtonClick(object sender, CustomerToolbarEventArgs e) {
      if (CustomersSet.SelectedItem != null) {
        CustomersSet.SelectedItem.CompanyName = e.Text;
      }
    }
  }
}

We only handle the one toolbar event, and just pull the required text from the CustomerToolbarEventArgs.

If you run this now, you’ll find that all three buttons modify the customer name property as expected.

After implementing this idea in my original application, the UI was much neater, as you can see from the screen shot below, and I had reduced the amount of code significantly. More to the point, maintenance was now pretty easy, as I had removed all the duplication.

My original Lightswitch application with the new toolbars in place

Obviously, there is a lot more that can be done here. The more I think about this, the more I realise that it opens up the possibilities of providing a custom UI for a Lightswitch application very easily, without requiring you to write a custom shell. Once again we see how Lightswitch gives you enormous leverage right out of the box, without sacrificing the flexibility that you need.