LightSwitch gives us the ability to import data from numerous sources. This data can then be shown to the end user in an editable grid by simply creating editable grid screens based off that data.
The column names of the grid will correspond to the field names of the table we import. This works for most cases but in certain situations we need these column names to be dynamic. For example, consider an application for the Top 5 cars of the year with database designed as follows:
CarRating
AttributeName | Car1 | Car2 | Car3 | Car4 | Car5 |
Braking | Good | Very Good | Good | Good | Good |
Acceleration | Excellent | Fair | Very Good | Very Good | Good |
Top Speed | Excellent | Excellent | Very Good | Good | Fair |
Handling | Very Good | Excellent | Fair | Fair | Good |
Car
Name | Ranking |
BMW | 5 |
Mercedes | 4 |
Porsche | 3 |
Aston Martin | 2 |
Ferrari | 1 |
Now we want to show the Car Rating table to the user but instead of “Car1”, Car2”, etc. in our column names we want to read the Cars table, get the top 5 cars and replace the column names with the names we get from the table.
In order to do that we start off with creating the 2 tables above: CarRating and Car. CarRating will have string fields called AttributeName, Car1, Car2, Car3, Car4, Car5 and the table “Car” will have a string field called Name and an Integer Field called Ranking.
Here is the snapshot of what the tables looks like in LightSwitch:
Next we create an editable grid screen for CarRating. So right click the screens node from the Solution Explorer and click Add Screen. Select the Editable Grid Template and choose CarRatings as your Screen Data and name your screen CarRatingsGridScreen.
Now from the Solution Explorer, right click the CarRatingsGridScreen and select View Screen Code.
This will bring you to the code editor. Replace all the code with the following (make sure the namespace name is consistent with the rest of the user code files):
VB
Imports System.Collections.Generic Imports Microsoft.LightSwitch Imports Microsoft.LightSwitch.Presentation Imports Microsoft.LightSwitch.Presentation.Extensions Imports System.Windows.Controls Namespace LightSwitchApplication Public Class CarRatingsGridScreen Private carRatingsGrid As DataGrid = Nothing Private Sub CarRatingsGridScreen_InitializeDataWorkspace(saveChangesTo As List(Of IDataService)) AddHandler Me.FindControl("grid").ControlAvailable, AddressOf CarRatingsGrid_ControlAvailable UpdateColumnNames() End Sub Private Sub CarRatingsGrid_ControlAvailable(sender As Object, e As ControlAvailableEventArgs) Me.carRatingsGrid = DirectCast(e.Control, DataGrid) End Sub Private Sub UpdateColumnNames() If Me.carRatingsGrid Is Nothing Then Throw New InvalidOperationException("Grid cannot be null") End If Dim top5Cars = Me.DataWorkspace.ApplicationData.Cars.OrderBy(Function(car) car.Ranking).Take(5) 'start with the second column Dim counter As Integer = 1 For Each car As Car In top5Cars Me.carRatingsGrid.Dispatcher.BeginInvoke( Sub() Dim rowTemplate As IContentItem = DirectCast(Me.carRatingsGrid.DataContext, IContentItem).ChildItems(0) Dim column As IContentItem = rowTemplate.ChildItems(counter) column.DisplayName = car.Name 'counter is incremented in Dispatcher.BeginInvoke counter += 1 End Sub ) Next End Sub End Class End Namespace
C#
using System; using System.Collections.Generic; using Microsoft.LightSwitch; using Microsoft.LightSwitch.Presentation; using Microsoft.LightSwitch.Presentation.Extensions; using System.Windows.Controls; namespace LightSwitchApplication { public partial class CarRatingsGridScreen { DataGrid carRatingsGrid = null; partial void CarRatingsGridScreen_InitializeDataWorkspace(List<IDataService> saveChangesTo) { this.FindControl("grid").ControlAvailable += CarRatingsGrid_ControlAvailable; UpdateColumnNames(); } void CarRatingsGrid_ControlAvailable(object sender, ControlAvailableEventArgs e) { this.carRatingsGrid = (DataGrid)e.Control; } private void UpdateColumnNames() { if (this.carRatingsGrid == null) throw new InvalidOperationException("Grid cannot be null"); var top5Cars = this.DataWorkspace.ApplicationData.Cars.OrderBy(car => car.Ranking).Take(5); //start with the second column int counter = 1; foreach (Car car in top5Cars) { this.carRatingsGrid.Dispatcher.BeginInvoke(() => { IContentItem rowTemplate = ((IContentItem)this.carRatingsGrid.DataContext).ChildItems[0]; IContentItem column = rowTemplate.ChildItems[counter]; column.DisplayName = car.Name; //counter is incremented in Dispatcher.BeginInvoke counter++; }); } } } }
In the code above, the first thing we are doing is registering CarRatingsGrid_ControlAvailable method with the ControlAvailable event of our DataGrid. This is happening in the InitializeDataWorkspace method.
When the DataGrid on the screen shows up, our CarRatingsGrid_ControlAvailable method will fire. This method has an argument of type ControlAvailableEventArgs from which we can get the actual Silverlight control. So we declare a DataGrid in our class and set it to e.Control in the method.
Next we call the UpdateColumnNames method from InitializeDataWorkspace. In UpdateColumnNames, we get the top 5 cars, traverse through each of them and set the names of the corresponding datagrid column.
Note that we are setting the column names of the datagrid on the UI thread. If we don’t include the Dispatcher.BeginInvoke, we will get an UnauthorizedAccessException. Secondly, since it is an async operation, we have to increase the counter in the Dispatcher.BeginInvoke otherwise we may get inconsistent results.
Now in order to verify that it works, you will need to add a few “Car” records. So create an editable grid screen for the Car table and add a few records. Now open a new instance of the CarRatingsGridScreen. It should have the updated column names.
Here is a screenshot of how the screens should look like at runtime:
Although this post is about setting the column names dynamically, it can be extended into any sort of UI manipulation. For example, we can get a handle to a textbox control in a list and details screen and set its background to yellow based on a computation or we can change the font color of a data grid cell. The same concept applies – get a handle to the underlying Silverlight control and change any property you want on the UI thread.
Happy Coding!!!