1 out of 5 rated this helpful - Rate this topic

Creating a LightSwitch Screen Template

LightSwitch includes templates that can be used to create screens that have common patterns. You can also develop templates that you can use with a specific data source, or with any data type.

A screen template generates data and controls for a screen, based on information that a developer specifies in the Add Screen dialog box. Screen templates can also be programmed to add code for the screen.

The techniques for creating HTML Client screen templates are similar to those shown here for Desktop Client screen templates. The primary differences are the ViewIDs, which differ for each client type. See LightSwitch Control ViewIDs.

Note Note

This document demonstrates a variety of scenarios that can be accomplished in screen templates, but it does not show how to produce an actual template that you can reuse. You can use these scenarios as best practices when you create templates. You can download a sample that contains both Desktop Client and HTML Client templates from the MSDN Code Gallery.

To create a screen template, first create a project by using the LightSwitch Extension Library project template, which is provided by the LightSwitch Extensibility Toolkit for Visual Studio 2013. When you create the project, select either Visual Basic or C# as the development language.

To define a screen template

  1. In Solution Explorer, choose the LibraryName.Lspkg project, where LibraryName is the name of your extension project.

  2. On the menu bar, choose Project, Add New Item.

  3. In the Add New Item dialog box, choose LightSwitch Screen Template.

  4. In the Name box, enter a name for the screen template. This is the name that developers will see in the Add Screen dialog box.

  5. Choose the OK button.

    A class that represents the screen template is added to ScreenTemplates folder in the Design project. The TemplateName.vb or TemplateName.cs file contains all of the code for the template. All of the examples in this document are in that file.

    Two image files are also added to the project, in the Resources\ScreenTemplateImages folder. These represent the images that will be displayed for the template in the Add Screen dialog box.

The DisplayName and Description properties provide the name and description that are displayed for the screen template in the Add Screen dialog box.

The following example shows the Description and DisplayName properties that are generated for a screen template named “Test Template”; you should change these to a meaningful name and description.

public string Description
        {
            get { return "TestTemplate Description"; }
        }

        public string DisplayName
        {
            get { return "Test Template"; }
        }

The RootDataSource property determines what types of data can be displayed by the screen template. The data types will be available in the Screen Data option in the Add Screen dialog box. The RootDataSource property has four valid values:

Collection

Enables the selection of collections or multiple result queries.

ScalarEntity

Enables the selection of one entity type, or queries that return one item.

NewEntity

Used for screens that are intended to enable creating new entities.

None

Used for screens in which no data is to be selected.

The following example shows Collection specified as the RootDataSource.

public RootDataSourceType RootDataSource
        {
            get { return RootDataSourceType.Collection; }
        }

In some situations, a screen template may have to display data that is related to the root data for the screen. For example, a screen template that is displaying an Order may also have to display related Order Lines. To enable this functionality, set the SupportsChildCollections property. If you set it to True, any related collections for the root data type will be displayed in the Add Screen dialog box so that a developer can decide whether to include them in a screen.

The following example shows the SupportsChildCollections property set to True; to disable child collections you should change this to False:

public bool SupportsChildCollections
        {
            get { return true; }
        }

Typically, most of the code for a screen template is in the Generate method. This method can be used to add data, code or controls to screens that are based on the template. LightSwitch provides basic screen elements such as screen commands. You can modify the content item hierarchy and create elements by using available storage model classes such as ContentItem and all its related classes.

LightSwitch provides a reference to the screen template generator host, which provides functionality that the generation code can use when it creates a screen. Some of this functionality is only available by using the host. If you are not sure which structures you should create, we recommend that you create an example screen in LightSwitch and then inspect the .lsml file in the project’s Common folder to see how LightSwitch generated it.

A content item represents a LightSwitch control on the screen. The following code in the Generate method shows how to add a content item to the root of the screen for the primary data member of the screen. This could be a collection or single entity, depending on the type of screen template.

ContentItem primaryDataControl = host.AddContentItem(host.ScreenLayoutContentItem, host.MakeUniqueLegalNameForContentItem("My Root Data"), host.PrimaryDataSourceProperty);

controls are identified by a ViewID, which is a combination of the name of the control and the name of the module where it is defined. For a list of ViewIDs, see LightSwitch Control ViewIDs.

The following code shows how to specify the control type of the ContentItem as a DataGrid.

host.SetContentItemView(primaryDataControl, "Microsoft.LightSwitch.RichClient:DataGrid");

LightSwitch uses smart defaults for things such as the control to use for a particular content item and its default property values. Smart defaults also consider the data type of a control, where it is in the content tree, what controls are its parents, and so on. If you set a content item to a specific control, LightSwitch honors that choice instead of using defaults (unless it is not valid). The data type of a particular field may be a custom type that you know nothing about, and have a default mapping to a control you know nothing about. Additionally, a developer may be able to override the controls that are selected by default for certain situations. Therefore, we recommend that you set a specific control for a content item only if you want that specific control to be used in all cases.

It is frequently necessary to expand a content item by adding children for all fields in its bound entity. We recommend that you call host.ExpandContentItem to accomplish this instead of doing it manually. This call generates the default children for that control automatically, in a consistent and correct manner. In the following example, ExpandContentItem adds the default child (the DataGridRow) to the DataGrid. It also adds the default children to the DataGridRow. This resembles the use of Reset in the LightSwitch screen designer.

host.ExpandContentItem(primaryDataControl);

LightSwitch supports an inheritance pattern for controls. Properties can be defined at different levels in the inheritance hierarchy. For example, all LightSwitch controls inherit from Microsoft.LightSwitch.RichClient:RootControl, which has common properties for control sizing (horizontal and vertical alignment, height and width, and so on). To set a property on a control, you have to know which control type that property was defined on. For a list of built-in LightSwitch control properties, see Defining, Overriding, and Using LightSwitch Control Properties.

The following example sets VerticalAlignment and HeightSizingMode properties on a screen control.

host.SetControlPropertyValue(primaryDataControl, "Microsoft.LightSwitch.RichClient:RootControl", "VerticalAlignment", "Top");
            host.SetControlPropertyValue(primaryDataControl, "Microsoft.LightSwitch.RichClient:RootControl", "HeightSizingMode", "Auto");

We recommend that you set values for properties only if you require that those specific values must be used, because doing this keeps LightSwitch from determining default values for the properties based on their context in the screen content tree.

The following example sets the display name for a content item. The example could also be used to set the display name for a screen member.

host.SetDisplayName(primaryDataControl, "Main Screen Data");

If no display name is set, LightSwitch provides a default display name, based on the data that the content item is bound to.

The following example sets the DisablePaging property on a screen data member. ScreenCollectionProperty represents a collection screen member; ScreenProperty represents a scalar type (entity or simple data type).

((ScreenCollectionProperty)host.PrimaryDataSourceProperty).DisablePaging = true;

The following example adds a string property to the screen.

// Add a screen member.
            ScreenProperty screenProperty = host.AddScreenProperty("Microsoft.LightSwitch:String", "NewScreenProperty") as ScreenProperty;

The following example adds the selected item of a screen collection to the screen.

// Get the data type of your property.
            ScreenCollectionProperty collectionProperty = (ScreenCollectionProperty)(host.PrimaryDataSourceProperty);
            IEntityType collectionDataType = host.FindGlobalModelItem<ISequenceType>(collectionProperty.PropertyType).ElementType as IEntityType; 
// Create an expression that represents accessing the selected item on your collection property.
            ChainExpression chain = host.CreateChainExpression(host.CreateMemberExpression(collectionProperty.Id));
            host.AppendMemberExpression(chain, "SelectedItem");

            // Add a content item representing the selected item and set its view to a RowsLayout.
            ContentItem detailControl = host.AddContentItem(host.ScreenLayoutContentItem, "ItemDetail", ContentItemKind.Details, chain, collectionDataType);
            host.SetContentItemView(detailControl, "Microsoft.LightSwitch.RichClient:RowsLayout");
            host.SetControlPropertyValue(detailControl, "Microsoft.LightSwitch.RichClient:RootControl", "VerticalAlignment", "Top");
            host.ExpandContentItem(detailControl);

The following example adds a group control the screen.

// Add a tabs group to the screen.
            ContentItem tabsGroup = host.AddContentItem(host.ScreenLayoutContentItem, "TabsGroup", ContentItemKind.Group);
            host.SetContentItemView(tabsGroup, "Microsoft.LightSwitch.RichClient:TabsLayout");

Additional related collections may be added to the screen if the developer specifies it. For example, the developer might want to include OrderLines when Orders are displayed. The following example shows how to add related collections to the screen. Notice that this code requires that you have already added the code shown in the previous example, "Add a Group Control".

foreach (ScreenCollectionProperty p in host.ChildCollectionProperties)
            {
                // Display each child collection as a grid.
                ContentItem currentTab = host.AddContentItem(tabsGroup, host.MakeUniqueLegalNameForContentItem("Tab"), p);
                host.SetContentItemView(currentTab, "Microsoft.LightSwitch.RichClient:DataGrid");
                host.ExpandContentItem(currentTab);
            }

If the primary data selected for the screen is a query, it may contain parameters. The following example demonstrates how to add parameters to a screen.

// Add parameters to the screen.
            ContentItem parameterGroup = host.AddContentItem(host.ScreenLayoutContentItem, "ParameterGroup", ContentItemKind.Group);
            host.SetContentItemView(parameterGroup, "Microsoft.LightSwitch.RichClient:ColumnsLayout");
            host.SetControlPropertyValue(parameterGroup, "Microsoft.LightSwitch.RichClient:RootControl", "VerticalAlignment", "Top");
            foreach (ScreenPropertyBase param in host.PrimaryDataSourceParameterProperties)
            {
                host.AddContentItem(parameterGroup, param.Name, param);
            }

You may have to identify and add a specific field from an entity to the screen. The following example illustrates how to find the definition of an entity and add a specific field from the selected item. Notice that the first code block duplicates code in an earlier example, "Display the Selected Item of a Collection."

// Find field called "Name".
            IEntityPropertyDefinition nameProperty = collectionDataType.Properties.Where(p => p.Name.ToLower() == "name").FirstOrDefault();
            if (nameProperty != null)
            {
                // Expression used to access collectionproperty.SelectedItem.Name
                ChainExpression nameExpression = host.CreateChainExpression(host.CreateMemberExpression(collectionProperty.Id));
                host.AppendMemberExpression(nameExpression, "SelectedItem");
                host.AppendMemberExpression(nameExpression, nameProperty.Name);
                ContentItem namePropertyControl = host.AddContentItem(host.ScreenLayoutContentItem,
                                                                             host.MakeUniqueLegalNameForContentItem("Name"),
                                                                             ContentItemKind.Value,
                                                                             nameExpression,
                                                                             nameProperty.PropertyType);
                host.SetControlPropertyValue(namePropertyControl, "Microsoft.LightSwitch.RichClient:RootControl", "VerticalAlignment", "Top");
            }

If your goal is to display all or most of the fields of an entity, a better option is to create a Detail node and use host.ExpandContentItem, and then modify the results if you have to.

You can use the host.AddScreenCodeBehind method to cause the template engine to add boilerplate code-behind to the screen as it is created. The developer can modify this code. You must supply code as a language-specific string. Therefore, you have to provide a code string for each language that you want to enable (currently, C# and Visual Basic are supported).The following example shows one way to do this, by using a dictionary that maps the coding language to a formatted string that contains the code for that language. First, a local property of type String is added to the screen, and then code is added behind the screen to set its value in the InitializeDataWorkspace method. The following example shows the dictionary that contains the code-behind for Visual Basic and C#.

private static Dictionary<CodeLanguage, String> _codeTemplates = new Dictionary<CodeLanguage, String>()
        {
            {CodeLanguage.CSharp,
                ""
                + "{0}namespace {1}"
                + "{0}{{"
                + "{0}    public partial class {2}"
                + "{0}    {{"
                + "{0}"
                + "{0}        partial void {2}_InitializeDataWorkspace(global::System.Collections.Generic.List<global::Microsoft.LightSwitch.IDataService> saveChangesTo)"
                + "{0}        {{"
                + "{0}            this.{3} = \"Hello World\";"
                + "{0}        }}"
                + "{0}"
                + "{0}    }}"
                + "{0}}}"
            },
            {CodeLanguage.VB,
                ""
                + "{0}Namespace {1}"
                + "{0}"
                + "{0}    Public Class {2}"
                + "{0}"
                + "{0}        Private Sub {2}_InitializeDataWorkspace(ByVal saveChangesTo As Global.System.Collections.Generic.List(Of Global.Microsoft.LightSwitch.IDataService))"
                + "{0}            Me.{3} = \"Hello World\""
                + "{0}        End Sub"
                + "{0}"
                + "{0}    End Class"
                + "{0}"
                + "{0}End Namespace"
            }
        };

The following code must also be added to the Generate method:

// Code Generation
            string codeTemplate = "";
            if (_codeTemplates.TryGetValue(host.ScreenCodeBehindLanguage, out codeTemplate))
            {
                host.AddScreenCodeBehind(String.Format(codeTemplate,
                                                       Environment.NewLine,
                                                       host.ScreenNamespace,
                                                       host.ScreenName,
                                                       screenProperty.Name));
            }

The following is a complete code listing for a screen template named “TestTemplate” that includes all of the code in this topic.

using System;
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.Linq;
using System.Management;
using System.Text;

using Microsoft.LightSwitch.Designers.ScreenTemplates.Model;
using Microsoft.LightSwitch.Model;
using Microsoft.LightSwitch.Model.Storage;

namespace ScreenTemplateExtension.ScreenTemplates
{
    public class TestTemplate : IScreenTemplate
    {
        #region IScreenTemplate Members

        public void Generate(IScreenTemplateHost host)
        {
            ContentItem primaryDataControl = host.AddContentItem(host.ScreenLayoutContentItem, host.MakeUniqueLegalNameForContentItem("My Root Data"), host.PrimaryDataSourceProperty);
            host.SetContentItemView(primaryDataControl, "Microsoft.LightSwitch: .RichClient DataGrid");
            host.ExpandContentItem(primaryDataControl);

            host.SetControlPropertyValue(primaryDataControl, "Microsoft.LightSwitch.RichClient:RootControl", "VerticalAlignment", "Top");
            host.SetControlPropertyValue(primaryDataControl, "Microsoft.LightSwitch.RichClient:RootControl", "HeightSizingMode", "Auto");
            host.SetDisplayName(primaryDataControl, "Main Screen Data");

            ((ScreenCollectionProperty)host.PrimaryDataSourceProperty).DisablePaging = true;

            // Get the data type of your property.
            ScreenCollectionProperty collectionProperty = (ScreenCollectionProperty)(host.PrimaryDataSourceProperty);
            IEntityType collectionDataType = host.FindGlobalModelItem<ISequenceType>(collectionProperty.PropertyType).ElementType as IEntityType;

            // Create an expression that represents accessing the selected item on your collection property.
            ChainExpression chain = host.CreateChainExpression(host.CreateMemberExpression(collectionProperty.Id));
            host.AppendMemberExpression(chain, "SelectedItem");

            // Add a content item representing the selected item and set its view to a RowsLayout.
            ContentItem detailControl = host.AddContentItem(host.ScreenLayoutContentItem, "ItemDetail", ContentItemKind.Details, chain, collectionDataType);
            host.SetContentItemView(detailControl, "Microsoft.LightSwitch.RichClient:RowsLayout");
            host.SetControlPropertyValue(detailControl, "Microsoft.LightSwitch.RichClient:RootControl", "VerticalAlignment", "Top");
            host.ExpandContentItem(detailControl);

            // Add parameters to the screen.
            ContentItem parameterGroup = host.AddContentItem(host.ScreenLayoutContentItem, "ParameterGroup", ContentItemKind.Group);
            host.SetContentItemView(parameterGroup, "Microsoft.LightSwitch.RichClient:ColumnsLayout");
            host.SetControlPropertyValue(parameterGroup, "Microsoft.LightSwitch.RichClient:RootControl", "VerticalAlignment", "Top");
            foreach (ScreenPropertyBase param in host.PrimaryDataSourceParameterProperties)
            {
                host.AddContentItem(parameterGroup, param.Name, param);
            }

            // Find field called "Name".
            IEntityPropertyDefinition nameProperty = collectionDataType.Properties.Where(p => p.Name.ToLower() == "name").FirstOrDefault();
            if (nameProperty != null)
            {
                // Expression used to access collectionproperty.SelectedItem.Name
                ChainExpression nameExpression = host.CreateChainExpression(host.CreateMemberExpression(collectionProperty.Id));
                host.AppendMemberExpression(nameExpression, "SelectedItem");
                host.AppendMemberExpression(nameExpression, nameProperty.Name);
                ContentItem namePropertyControl = host.AddContentItem(host.ScreenLayoutContentItem,
                                                                             host.MakeUniqueLegalNameForContentItem("Name"),
                                                                             ContentItemKind.Value,
                                                                             nameExpression,
                                                                             nameProperty.PropertyType);
                host.SetControlPropertyValue(namePropertyControl, "Microsoft.LightSwitch.RichClient:RootControl", "VerticalAlignment", "Top");
            }

            // Add a tabs group to the screen.
            ContentItem tabsGroup = host.AddContentItem(host.ScreenLayoutContentItem, "TabsGroup", ContentItemKind.Group);
            host.SetContentItemView(tabsGroup, "Microsoft.LightSwitch.RichClient:TabsLayout");

            foreach (ScreenCollectionProperty p in host.ChildCollectionProperties)
            {
                // Display each child collection as a grid.
                ContentItem currentTab = host.AddContentItem(tabsGroup, host.MakeUniqueLegalNameForContentItem("Tab"), p);
                host.SetContentItemView(currentTab, "Microsoft.LightSwitch.RichClient:DataGrid");
                host.ExpandContentItem(currentTab);
            }

            // Add a screen member.
            ScreenProperty screenProperty = host.AddScreenProperty("Microsoft.LightSwitch:String", "NewScreenProperty") as ScreenProperty;

            // Code Generation
            string codeTemplate = "";
            if (_codeTemplates.TryGetValue(host.ScreenCodeBehindLanguage, out codeTemplate))
            {
                host.AddScreenCodeBehind(String.Format(codeTemplate,
                                                       Environment.NewLine,
                                                       host.ScreenNamespace,
                                                       host.ScreenName,
                                                       screenProperty.Name));
            }
        }

        private static Dictionary<CodeLanguage, String> _codeTemplates = new Dictionary<CodeLanguage, String>()
        {
            {CodeLanguage.CSharp,
                ""
                + "{0}namespace {1}"
                + "{0}{{"
                + "{0}    public partial class {2}"
                + "{0}    {{"
                + "{0}"
                + "{0}        partial void {2}_InitializeDataWorkspace(global::System.Collections.Generic.List<global::Microsoft.LightSwitch.IDataService> saveChangesTo)"
                + "{0}        {{"
                + "{0}            this.{3} = \"Hello World\";"
                + "{0}        }}"
                + "{0}"
                + "{0}    }}"
                + "{0}}}"
            },
            {CodeLanguage.VB,
                ""
                + "{0}Namespace {1}"
                + "{0}"
                + "{0}    Public Class {2}"
                + "{0}"
                + "{0}        Private Sub {2}_InitializeDataWorkspace(ByVal saveChangesTo As Global.System.Collections.Generic.List(Of Global.Microsoft.LightSwitch.IDataService))"
                + "{0}            Me.{3} = \"Hello World\""
                + "{0}        End Sub"
                + "{0}"
                + "{0}    End Class"
                + "{0}"
                + "{0}End Namespace"
            }
        };

        public string Description
        {
            get { return "TestTemplate Description"; }
        }

        public string DisplayName
        {
            get { return "Test Template"; }
        }

        public Uri PreviewImage
        {
            get { return new Uri("/ScreenTemplateExtension.Design;component/Resources/ScreenTemplateImages/TestTemplateLarge.png", UriKind.Relative); }
        }

        public RootDataSourceType RootDataSource
        {
            get { return RootDataSourceType.Collection; }
        }

        public string ScreenNameFormat
        {
            get { return "{0}TestTemplate"; }
        }

        public Uri SmallIcon
        {
            get { return new Uri("/ScreenTemplateExtension.Design;component/Resources/ScreenTemplateImages/TestTemplateSmall.png", UriKind.Relative); }
        }

        public bool SupportsChildCollections
        {
            get { return true; }
        }

        public string TemplateName
        {
            get { return TestTemplate.TemplateId; }
        }

        #endregion

        #region Constants

        internal const string TemplateId = "ScreenTemplateExtension:TestTemplate";

        #endregion
    }

    [Export(typeof(IScreenTemplateFactory))]
    [Template(TestTemplate.TemplateId)]
    internal class TestTemplateFactory : IScreenTemplateFactory
    {
        #region IScreenTemplateFactory Members

        IScreenTemplate IScreenTemplateFactory.CreateScreenTemplate()
        {
            return new TestTemplate();
        }

        #endregion
    }
}
Did you find this helpful?
(1500 characters remaining)
Thank you for your feedback
Show:
© 2014 Microsoft. All rights reserved.