Dynamically setting column names on an editable grid (Babar Ismail)

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:

image

image

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.

image

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 System.Linq
Imports Microsoft.LightSwitch
Imports Microsoft.LightSwitch.Presentation
Imports Microsoft.LightSwitch.Presentation.Extensions
Imports System.Windows.Controls
Imports Microsoft.LightSwitch.Threading
Namespace LightSwitchApplication
    Partial Public Class CarRatingsGridScreen

        Private Sub CarRatingsGridScreen_Created()
            AddHandler Me.FindControl("grid").ControlAvailable, AddressOf CarRatingsGrid_ControlAvailable
        End Sub

        Private Sub CarRatingsGrid_ControlAvailable(sender As Object, e As ControlAvailableEventArgs)
            UpdateColumnNames(DirectCast(e.Control, DataGrid))
        End Sub

        Private Sub UpdateColumnNames(carRatingsGrid As DataGrid)
            Me.Details.Dispatcher.BeginInvoke(
                Sub()

                    Dim top5Cars = Me.DataWorkspace.ApplicationData.Cars.OrderBy(Function(car) car.Ranking).Take(5).Execute()

                    Dim carNames As String() = top5Cars.[Select](Function(car) car.Name).ToArray()

                    Dispatchers.Main.BeginInvoke(
                        Sub()
                            Dim rowTemplate As IContentItem = DirectCast(carRatingsGrid.DataContext, IContentItem).ChildItems(0)

                            For index As Integer = 0 To carNames.Length - 1
                                'start with the second column of the grid
                                rowTemplate.ChildItems(index + 1).DisplayName = carNames(index)
                            Next

                        End Sub)

                End Sub)
        End Sub
    End Class
End Namespace

C#

using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.LightSwitch;
using Microsoft.LightSwitch.Presentation;
using Microsoft.LightSwitch.Presentation.Extensions;
using System.Windows.Controls;
using Microsoft.LightSwitch.Threading;
namespace LightSwitchApplication
{
    public partial class CarRatingsGridScreen
    {
        partial void CarRatingsGridScreen_Created()
        {
            this.FindControl("grid").ControlAvailable += CarRatingsGrid_ControlAvailable;
        }

        void CarRatingsGrid_ControlAvailable(object sender, ControlAvailableEventArgs e)
        {            
            UpdateColumnNames((DataGrid)e.Control);
        }

        private void UpdateColumnNames(DataGrid carRatingsGrid)
        {
            this.Details.Dispatcher.BeginInvoke(() =>
            {                

                var top5Cars = this.DataWorkspace.ApplicationData.Cars.OrderBy(car => car.Ranking).Take(5).Execute();
                
                string[] carNames = top5Cars.Select(car => car.Name).ToArray();

                Dispatchers.Main.BeginInvoke(() =>
                {
                    IContentItem rowTemplate = ((IContentItem)carRatingsGrid.DataContext).ChildItems[0];
                                        
                    for (int index = 0; index < carNames.Length; index++)
                    {
                        //start with the second column of the grid
                        rowTemplate.ChildItems[index + 1].DisplayName = carNames[index];
                    }
                });
            });
        }
    }
}
 

In the code above, the first thing we are doing is registering CarRatingsGrid_ControlAvailable event handler with the ControlAvailable event of our DataGrid. This is happening in the CarRatingsGridScreen_Created 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.

Next we pass in the control to the UpdateColumnNames method from CarRatingsGrid_ControlAvailable. Since the method will be executed on the Main thread, we go to the logical thread to get the top 5 car names and store them in an array (we cannot access data from the DataWorkspace on the Main thread). Then we go back to the Main thread and set the column names on the grid.

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:

image

 

image

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!!!