Walkthrough: Creating a Detail Control Extension

This walkthrough demonstrates how to create a detail control extension for Visual Studio LightSwitch. Detail controls are used by LightSwitch to display an entity field. This differs from a Group control, which can be used to display the whole entity by displaying each of the entity’s fields in an arranged manner. The LightSwitch Summary control is one example of a detail control.

To create a detail control extension, you must perform the following tasks:

  • Visual Studio 2013 Professional

  • Visual Studio 2013 SDK

  • LightSwitch Extensibility Toolkit for Visual Studio 2013

The first step is to create a project and add a Control template.

To create an extension project

  1. On the menu bar in Visual Studio, choose File, New, Project.

  2. In the New Project dialog box, expand the Visual Basic or Visual C# node, expand the LightSwitch node, choose the Extensibility node, and then choose the LightSwitch Extension Library template.

  3. In the Name field, enter DetailControlExtension as the name for your extension library. This name will appear on the Extensions tab of the LightSwitch Application Designer.

  4. Choose the OK button to create a solution that contains the seven projects that are required for the extension.

To choose an extension type

  1. In Solution Explorer, choose the DetailControlExtension.Lspkg project.

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

  3. In the Add New Item dialog box, choose Control.

  4. In the Name field, enter DetailControl as the name for your extension. This name will appear in the LightSwitch screen designer.

  5. Choose the OK button. Files will be added to several projects in your solution.

Two image files named DetailControl.png were added to your solution, one in the ControlImages folder of the DetailControlExtension.Client.Design project and the other in the ControlImages folder of the DetailControlExtension.Design project. The image in the file is used as an icon. You can replace the default image with one that uniquely identifies your control.

To modify the icon image

  1. In Solution Explorer, on the shortcut menu for the DetailControl.png file in the ControlImages folder of the DetailControlExtension.Client.Design project, choose Open With.

  2. In the Open With dialog box, choose Paint, and then choose the OK button.

  3. In Paint, change the image; for example, change the color or add a shape, and then save the file and return to Visual Studio.

  4. Choose the DetailControl.png file, and then on the menu bar, choose Edit, Copy.

  5. Choose the ControlImages folder of the DetailControlExtension.Design project, and then on the menu bar, choose Edit, Paste. In the message that asks whether you want to replace the file, choose the Yes button.

Metadata that defines the control is contained in the .lsml file in the DetailControlExtension.Common project. For a detail control, the important elements are the SupportedContentItemKind, the DisplayName, and the DisplayProperty.

The SupportedContentItemKind tells LightSwitch the type of the control; for a detail control, set SupportedContentItemKindto Details.

The DisplayName property provides the name that is displayed in the screen designer. It can be defined as a String, for example “My Detail Control,” or as String resources in the ModuleResources file by using the notation $(DetailControl_DisplayName).

DisplayProperty provides a property that the developer can set at design time to specify which entity field to display.

To update the control metadata

  1. In Solution Explorer, in the Controls folder of the DetailControlExtension.Common project, open the shortcut menu for the DetailControl.lsml file and choose Open With.

  2. In the Open With dialog box, choose XML (Text) Editor, and then choose the OK button.

  3. Change the SupportedContentItemKind element to SupportedContentItemKind="Details".

    This tells LightSwitch that the control extension is a details control.

  4. Change the DisplayName element to <DisplayName Value="$(DetailControl_DisplayName)" />.

    In this case the control will get the display name from a string resource in a resource file that you will create in a later step.

  5. Insert the following code after the Control.Attributes block.

    <Control.Properties>
          <!-- 
            Define 'DisplayProperty' to allow the developer to select which entity field should be shown inside the detail control. 
          -->
          <ControlProperty Name="DisplayProperty"
                           PropertyType=":String"
                           CategoryName="Appearance"
                           EditorVisibility="PropertySheet">
            <ControlProperty.Attributes>
              <!-- Reference localized strings in ModuleResource.resx -->
              <DisplayName Value="$(DisplayProperty_DisplayName)" />
              <Description Value="$(DisplayProperty_Description)" />
            </ControlProperty.Attributes>
    
            <!-- Define the default value of this property to be an empty string. -->
            <ControlProperty.DefaultValueSource>
              <ScreenExpressionTree>
                <!-- Only a constant expression is supported here. -->
                <ConstantExpression ResultType=":String" Value=""/>
              </ScreenExpressionTree>
            </ControlProperty.DefaultValueSource>
    
          </ControlProperty>
        </Control.Properties>
    

    This code tells LightSwitch to display a String property in the property sheet. The DisplayName and Description attributes are again defined as String resources in a resource file. The DefaultValueSource element tells LightSwitch to display an empty String by default.

  6. Delete the Control.SupportedDataTypes block. Because a group control cannot directly display data, this block is unnecessary.

  7. The complete code for the DetailControl.lsml file should now resemble the following example.

    <?xml version="1.0" encoding="utf-8" ?>
    <ModelFragment
      xmlns="http://schemas.microsoft.com/LightSwitch/2010/xaml/model"
      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
      
      <Control Name="DetailControl"
        SupportedContentItemKind="Details"
        DesignerImageResource="DetailControlExtension.DetailControl::ControlImage">
        <Control.Attributes>
          <DisplayName Value="$(DetailControl_DisplayName)" />
        </Control.Attributes>
        <Control.Properties>
          <!-- 
            Define 'DisplayProperty' to allow the developer to select which entity field should be shown inside the detail control. 
          -->
          <ControlProperty Name="DisplayProperty"
                           PropertyType=":String"
                           CategoryName="Appearance"
                           EditorVisibility="PropertySheet">
            <ControlProperty.Attributes>
              <!-- Reference localized strings in ModuleResource.resx -->
              <DisplayName Value="$(DisplayProperty_DisplayName)" />
              <Description Value="$(DisplayProperty_Description)" />
            </ControlProperty.Attributes>
    
            <!-- Define the default value of this property to be an empty string. -->
            <ControlProperty.DefaultValueSource>
              <ScreenExpressionTree>
                <!-- Only a constant expression is supported here. -->
                <ConstantExpression ResultType=":String" Value=""/>
              </ScreenExpressionTree>
            </ControlProperty.DefaultValueSource>
    
          </ControlProperty>
        </Control.Properties>
             </Control>
    </ModelFragment>
    

The ModuleResources.resx file in the DetailControlExtension.Common project contains resources that are used by the control. You can add string resources for the text that will be displayed for your control in the screen designer, in this case, the three strings that you defined in the .lsml file.

To add resource strings

  1. In Solution Explorer, expand the Resources node in the DetailControlExtension.Common project, and then open the ModuleResources.resx file.

  2. Add the following values to the ModuleResources.resx file.

    Name

    Value

    Comments

    DetailControl_DisplayName

    My Detail Control

    The control’s display name in the screen designer.

    DisplayProperty_Description

    The property shown in the application.

    Short description string for the property in the screen designer.

    DisplayProperty_DisplayName

    Display Property

    Property name in the screen designer.

The DetailControl.xaml and DetailControl.xaml.vb or DetailControl.xaml.cs files in the Presentation, Controls folder of the DetailControlExtension.Client project contain the implementation for the control. The template creates a default implementation, which you will want to replace with your own code.

A dependency property lets you later switch the control from a user control to a Silverlight custom control with a similar implementation. This would allow the control to be restyled via a theme extension, and is the preferred method for creating a control extension. The dependency property will be used by the developer to bind to an entity property at design time, defining what will be displayed by the control.

To define a dependency property

  1. In Solution Explorer, in the Controls folder of the DetailControlExtension.Client project, open the DetailControl.xaml.vb or DetailControl.xaml.cs file.

  2. Add the following Imports statements to the DetailControl.xaml.vb file, or using statements to the DetailControl.xaml.cs file.

    No code example is currently available or this language may not be supported.
  3. Add a EntityProperty property inside the DetailControl class, as follows.

    No code example is currently available or this language may not be supported.

Next, create the user interface for the control in the DetailControl.xaml file. The code will define a DetailRoot element and set DataContext to that DetailRoot. It will also define a TextBoxcontrol named DetailTextBox and set its Text and ReadOnly bindings to the EntityProperty dependency property that you created earlier.

To create the control UI

  1. In Solution Explorer, in the Controls folder of the DetailControlExtension.Client project, open the DetailControl.xaml file.

  2. Replace the existing XAML code with the following code.

    <UserControl x:Class="DetailControlExtension.Presentation.Controls.DetailControl"
                 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                 x:Name="DetailRoot"
                 HorizontalAlignment="Stretch"
                 >
        <!-- 
            The DataContext of the control is a ContentItem.  However, you defined several DependencyProperties inside your control to expose necessary data to be shown on the UI.
            Here, set the DataContext of the internal implementation to your control itself, so you can use short DataBindings inside.
        -->
        <Grid DataContext="{Binding ElementName=DetailRoot}" HorizontalAlignment="Stretch">
            <!-- 
            That is the TextBox you show the property value.  The EntityProperty is a dependency property that will bind to the property to be shown on the UI.
            Bind its Value and IsReadOnly properties to the text box.
            -->
            <TextBox Name="DetailTextBox"
                     HorizontalAlignment="Stretch"
                     VerticalAlignment="Stretch"
                     Text="{Binding EntityProperty.Value, Mode=TwoWay}" 
                     IsReadOnly="{Binding EntityProperty.IsReadOnly}" />
        </Grid>
    </UserControl>
    

Next, you will add code to get the value for the EntityProperty property. The value is calculated using the current entity and the DisplayProperty control property. The current entity is loaded in the LightSwitch runtime application, and the DisplayProperty value is set by the application developer in the screen designer or in code. To get these two values and monitor changes correctly, you must add two more dependency properties, DisplayPropertyName and ContentItem, to the DetailControl class.

To bind the property

  1. In Solution Explorer, in the Controls folder of the DetailControlExtension.Client project, open the DetailControl.xaml.vb or DetailControl.xaml.cs file.

  2. Add the following code for the dependency properties after the EntityProperty code block:

    No code example is currently available or this language may not be supported.

    You also have to set the binding for DisplayPropertyName and ContentItem when the ControlName is created.

  3. Add the following code inside the DetailControl constructor after the InitializeComponent call.

    No code example is currently available or this language may not be supported.

    You also have to add a SetContentDataBinding function to the class.

  4. Add the following code to the DetailControl class after the OnContentItemChanged block.

    No code example is currently available or this language may not be supported.

    The implementation of the detail control is now complete, and you can test it in LightSwitch.

You can test the detail control in an experimental instance of Visual Studio. If you have not already tested another LightSwitch extensibility project, you have to enable the experimental instance first.

To enable an experimental instance

  1. In Solution Explorer, choose the BusinessTypeExtension.Vsix project.

  2. On the menu bar, choose Project, BusinessTypeExtension.Vsix Properties.

  3. On the Debug tab, under Start Action, choose Start external program.

  4. Enter the path of the Visual Studio executable, devenv.exe.

    By default on a 32-bit system, the path is C:\Program Files\Microsoft Visual Studio 12.0\Common7\IDE\devenv.exe; on a 64-bit system, it is C:\Program Files (x86)\Microsoft Visual Studio 12.0\Common7\IDE\devenv.exe.

  5. In the Command line arguments field, enter /rootsuffix Exp.

    Note Note

    All subsequent LightSwitch extensibility projects will also use this setting, by default.

To test the detail control

  1. On the menu bar, choose Debug, Start Debugging. An experimental instance of Visual Studio opens.

  2. In the experimental instance, on the menu bar, choose File, New, Project.

  3. In the New Project dialog box, expand the Visual Basic or Visual C# node, choose the LightSwitch node, and then choose the LightSwitch Desktop Application template.

  4. In the Name field, enter DetailControlTest, and then choose the OK button to create a test project.

  5. On the menu bar, choose Project, DetailControlTest Properties.

  6. In the Project Designer, on the Extensions tab, select the DetailControlExtension check box.

  7. Create a basic LightSwitch application that has a table that contains several String fields, or connect to an external data source.

  8. Add a List and Details screen, and then in the screen designer, change the Summary control below the first Rows Layout element to My Detail Control.

  9. In the Properties window, select the Display Property property, and then enter the name of a field in the table.

  10. On the menu bar, choose Debug, Start Debugging. Observe the behavior of the My Detail Control control in the application and verify that the data for the field is properly displayed.

The current implementation of the control requires the application developer to set the DisplayProperty correctly. Otherwise, nothing will be displayed. To make the control easier to use, you can remove this requirement. If the application developer does not supply a property, you should supply the Summary property of the entity type automatically.

To do this, create a GetSummaryMethod function, add several utility methods, and add code to calculate the default Summary property.

To create a GetSummaryMethod function

  1. In Solution Explorer, in the Controls folder of the DetailControlExtension.Client project, open the DetailControl.xaml.vb or DetailControl.xaml.cs file.

  2. Add the following code to the DetailControl class.

    No code example is currently available or this language may not be supported.

    In this code, the Summary property definition is extracted from the application metadata if the user defines it in the application. You are still missing the logic to calculate the default summary property if it is not set explicitly by the application developer. You will add this logic next.

  3. Locate the SetContentDataBinding() method in the DetailControl class. Replace the If String.IsNullOrEmpty (Visual Basic) or if (string.IsNullOrEmpty (C#) block with the following:

    No code example is currently available or this language may not be supported.

    Next, you will add some utility methods that can be used in both the control and also in the designer extension you will add later. To share the code between different projects, add it to the Common project.

To add utility methods

  1. In Solution Explorer, choose the DetailControlExtension.Common project.

  2. On the menu bar, choose Project, Add Class.

  3. In the Add New Item dialog box, choose Class.

  4. In the Name field, enter ModelUtilities as the name for your class file, and then choose the OK button.

  5. Replace the contents of the class file with the following.

    No code example is currently available or this language may not be supported.

    Next, add a reference to the Common project from the Client project. This will enable you to use the utility functions defined in the Common project from the Client project.

To add a reference

  1. In Solution Explorer, open the shortcut menu for the DetailControl.Client project, and then choose Add Reference.

  2. In the Add Reference dialog box, choose the Projects tab and add a reference to the DetailControl.Common project.

At this point, you can test the control again and observe the changes in behavior. Verify that the summary property is displayed if no value is specified for the DisplayProperty.

The remainder of this walkthrough covers techniques for adding optional capabilities to your control.

All of the built-in LightSwitch controls are programmable. To enable developers to access the control in their application code, you will want to implement the IContentVisual interface for your control.

To implement the IContentVisual interface

  1. In Solution Explorer, in the Controls folder of the DetailControlExtension.Client project, open the DetailControl.xaml.vb or DetailControl.xaml.cs file.

  2. Update the class definition to implement IContentVisual.

    No code example is currently available or this language may not be supported.
  3. Add the implementation to the class.

    No code example is currently available or this language may not be supported.

So far your detail control requires the application developer to enter a valid entity field name in the Display Property text box in the Properties window. A better experience would be to provide a drop-down list with all valid choices. You can do so by creating a custom property editor.

For control extensions, two property editors are required; the LightSwitch screen designer and the run-time screen editor are built on different platforms. Working in the screen designer requires a Windows Presentation Foundation (WPF) based editor. Working in customization mode requires a Silverlight based editor.

A WPF-based property editor provides access to properties from the screen designer. When you create an extension project by using the LightSwitch Extension Library template, a Design project is added to the solution. This project generates an assembly based on the full .NET Framework, and this is where you will create a WPF-based property editor.

The first step is to add references to the LightSwitch Designer namespace and to the Common project in your solution.

To add references

  1. In Solution Explorer, open the shortcut menu for the DetailControl.Design project, and then choose Add Reference.

  2. In the Add Reference dialog box, add a reference to Microsoft.LightSwitch.Design.Designer.dll.

    You can find the assembly in the PrivateAssembly folder under the Visual Studio IDE folder.

  3. Open the shortcut menu for the DetailControl.Design node, and then choose Add Existing Item.

  4. Choose the Common project in your solution, and then select the ModelUtilities file.

  5. Expand the drop-down list on the Add button, and then choose Add As Link.

The next step is to create the WPF control that will be used to edit the property.

To create the WPF control

  1. In Solution Explorer, open the shortcut menu for the DetailControl.Design project, and then choose Add New Folder. Name the new folder Editors.

  2. In the Editors folder, open the shortcut menu and choose Add New Item.

  3. In the Add New Item dialog box, expand the WPF node, and choose User Control (WPF).

  4. In the Name field, enter EntityPropertyPicker, and then click Add.

    A EntityPropertyPicker.xaml file and a EntityPropertyPicker.xaml.vb or EntityPropertyPicker.xaml.cs file will be added to the Editors folder.

  5. In the Editors folder, open the shortcut menu and choose Add New Item.

  6. In the Add New Item dialog box, expand the Code node and choose Class.

  7. In the Name field, enter EntityPropertyPickerEditor and then click Add.

  8. Open the EntityPropertyPickerEditor class file and replace the contents with the following code:

    No code example is currently available or this language may not be supported.

    This code implements a component that can be loaded by a LightSwitch designer based on its name inside a PropertyValueEditorName attribute. When a LightSwitch designer displays a property marked to use an editor with the same name, it will create a WPF control with the DataTemplate and host it in the property sheet. The DataContext of the control will be set to an IBindablePropertyEntry object, where the control can get or set the value of the property.

    The next step is to change the control property metadata in the .lsml file for the extension.

  9. In the Common project, open the DetailControl.lsml file.

  10. Add the UIEditorID property to the ControlProperty element, as follows.

    <ControlProperty Name="DisplayProperty"
                           PropertyType=":String"
                           CategoryName="Appearance"
                           UIEditorId="DetailControlExtension:EntityPropertyPicker"
                           EditorVisibility="PropertySheet">
    
  11. In the Design project, open the EntityPropertyPicker.xaml file, and replace the default code with the following code to implement the control.

    <UserControl x:Class="DetailControlExtension.Editors.EntityPropertyPicker"
                 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                 xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
                 xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
                 mc:Ignorable="d" 
                 >
        <!-- 
        DesignTimeFontSize and DesignTimeFontFamily are design-time public resource items.  
        Use them to ensure that you use the same font inside different controls on the property sheet.
        -->
        <Grid TextBlock.FontSize="{DynamicResource DesignTimeFontSize}"
              TextBlock.FontFamily="{DynamicResource DesignTimeFontFamily}"
    >
            <Grid.RowDefinitions>
                <RowDefinition Height="Auto" />
                <RowDefinition Height="Auto" />
            </Grid.RowDefinitions>
    
            <!-- 
                That is the label to show the property name.  
                The DataContext of this control is an IBindablePropertyEntry object. Use its Entry property to get the IPropertyEntry.
            -->
            <TextBlock x:Name="ComboBoxLabel"
                       Text="{Binding Entry.DisplayName, Mode=OneWay}"
                       TextWrapping="WrapWithOverflow"
                       ToolTip="{Binding Entry.Description, Mode=OneWay}"
                       Margin="0,0,0,2"/>
    
            <!-- 
                This is the ComboBox to pick up the value.  The DataContext of this control is an IBindablePropertyEntry object. Use its Entry property to get the IPropertyEntry.
                Use the 'GetTextPropertiesConverter' to collect all valid choices.  The PropertyValue.Value is the value of the DisplayProperty of the control.
            -->
            <ComboBox x:Name="ComboBox" 
                      Grid.Row ="1"
                      SelectedItem="{Binding Entry.PropertyValue.Value}"
                      >
            </ComboBox>
        </Grid>
                  
    </UserControl>
    

    The XAML code adds a Label to show the property name, and a ComboBox to show the current value. Next, you will add some code to get the list for the ComboBox. To do so, you must know the exact entity type that is bound to the control.

    In the screen designer, the context of the property is exposed by IPropertyValue.ModelItem. For a control property, ModelItem is always the instance of IContentItemDefinition where the control is being used. There, a single property, IContentItemDefinition.DataType, defines which entity type is bound to the control. For the ComboBox, a list of the names of all properties in that entity type is needed. This list can be generated by creating a converter.

  12. Close and save the EntityPropertyPicker.xaml file.

Next, define the Editors namespace and add code to initialize the editor in the code-behind file.

To add code

  1. Expand the EntityPropertyPicker.xaml node, open the EntityPropertyPicker.xaml.vb or EntityPropertyPicker.xaml.cs file, and replace the contents with the following.

    No code example is currently available or this language may not be supported.
  2. Close and save the EntityPropertyPicker.xaml.vb or EntityPropertyPicker.xaml.cs file.

Next, add another utility method to get a collection of entity properties.

To add a utility method

  1. In the Common project, open the ModelUtilities class file.

  2. Add a new method to the class.

    No code example is currently available or this language may not be supported.

    This method retrieves a collection of properties that can be represented as a string inside a data type, including an entity type.

Next, create a converter that will use the GetAllTextProperties method to get the names of all properties in the entity so that they can be displayed in the ComboBox.

To create the GetTextPropertiesConverter

  1. In the Editors folder of the Design project, open the shortcut menu and choose Add New Item.

  2. In the Add New Item dialog box, expand the Code node, and choose Class.

  3. In the Name field, enter GetTextPropertiesConverter, and then click Add.

  4. Open the GetTextPropertiesConverter class file and add the following code.

    No code example is currently available or this language may not be supported.

    This converter takes the IContentItemDefinition, which can be accessed from IPropertyValue, and returns a collection of valid property names plus an empty string, which represents the default value of the property, meaning nothing has been chosen.

  5. In the Editors folder, open the EntityPropertyPicker.xaml file.

  6. Add the following namespace mapping to the control.

    xmlns:e="clr-namespace:DetailControlExtension.Editors"
    
  7. Add a resource to create the new converter.

    <UserControl.Resources>
            <e:GetTextPropertiesConverter x:Key="GetTextPropertiesConverter" />
        </UserControl.Resources>
    
    NoteNote

    At this point you might notice an error in the designer. This error will be resolved when you build the Client project.

  8. Update the ComboBox element with the new Converter by setting the ItemsSource property:

    <ComboBox x:Name="ComboBox" 
                      Grid.Row ="1"
                      SelectedItem="{Binding Entry.PropertyValue.Value}"
                      ItemsSource="{Binding Entry.PropertyValue.ModelItem, Mode=OneWay, Converter={StaticResource GetTextPropertiesConverter}}"
                      >
            </ComboBox>
    

Next, create another converter to format the property name.

To create a GetTextPropertiesConverter

  1. In the Editors folder of the Design project, open the shortcut menu and choose Add New Item.

  2. In the Add New Item dialog box, expand the Code node, and choose Class.

  3. In the Name field, enter DisplayNameConverter, and then click Add.

  4. Open the DisplayNameConverter class file and add the following code.

    No code example is currently available or this language may not be supported.

    This code formats the String with the “:” notation, which is a shortcut for "Microsoft.LightSwitch:".

  5. In the Editors folder, open the EntityPropertyPicker.xaml file.

  6. Add a resource to create the new converter.

    <UserControl.Resources>
            <e:GetTextPropertiesConverter x:Key="GetTextPropertiesConverter" />
            <e:DisplayNameConverter x:Key="DisplayNameConverter" />
        </UserControl.Resources>
    
  7. On the menu bar, choose Build, DetailControlExtension.Design.

  8. Add a converter block to the TextBlock element.

    <TextBlock x:Name="ComboBoxLabel"
                       Text="{Binding Entry.DisplayName, Mode=OneWay, Converter={StaticResource DisplayNameConverter}}"
                       TextWrapping="WrapWithOverflow"
                       ToolTip="{Binding Entry.Description, Mode=OneWay}"
                       Margin="0,0,0,2"/>
    

Next, add another converter to display user-friendly default text in the ComboBox when no entity field is selected.

To create a PropertyNameToDisplayNameConverter

  1. In the Resources folder of the Design project, open the shortcut menu and choose Add New Item.

  2. In the Add New Item dialog box, expand the General node, and choose Resources file.

  3. In the Name field, enter DesignResources, and then click Add.

  4. In the DesignResources.resx file, add a string named “DefaultProperty_DisplayName”, and assign it a value of <Summary>.

    This resource string will be used by the converter when no value has been specified by the application developer.

  5. In the Editors folder of the Design project, open the shortcut menu and choose Add New Item.

  6. In the Add New Item dialog box, expand the Code node and choose Class.

  7. In the Name field, enter PropertyNameToDisplayNameConverter, and then click Add.

  8. In the PropertyNameToDisplayNameConverter class file, add the following code.

    No code example is currently available or this language may not be supported.

    This code checks to see whether the value of the ComboBox is a null String, and if it is, it replaces it with the DefaultProperty_DisplayName resource string.

  9. In the Editors folder, open the EntityPropertyPicker.xaml file.

  10. Add a resource to create the new converter, as follows.

    <UserControl.Resources>
            <e:GetTextPropertiesConverter x:Key="GetTextPropertiesConverter" />
            <e:DisplayNameConverter x:Key="DisplayNameConverter" />
            <e:PropertyNameToDisplayNameConverter x:Key="PropertyNameToDisplayNameConverter" />    
    </UserControl.Resources>
    
  11. Add an ItemTemplate block inside the ComboBox element, as follows.

    <ComboBox.ItemTemplate>
                    <DataTemplate>
                        <!-- The 'PropertyNameToDisplayNameConverter' is used to convert the empty string to a developer friendly string. -->
                        <TextBlock Text="{Binding Converter={StaticResource PropertyNameToDisplayNameConverter}}" />
                    </DataTemplate>
                </ComboBox.ItemTemplate>
    

The property editor is now finished. You can test it in an experimental instance of Visual Studio and observe that the property editor displays a list of entity fields.

If you try to set the DisplayProperty in the run-time screen designer, you will notice that the property sheet still displays a TextBox for the property. So far you have only created a WPF property editor. The run-time screen designer is based on Silverlight. Therefore, it does not recognize the WPF-based editor; you will have to create a Silverlight equivalent.

The DetailControlExtension.Client.Design project in the solution is where you will write code for the run-time screen designer. You can share some of the code that you added to the DetailControlExtension.Design project; the implementation of the Silverlight editor resembles that of the WPF editor.

The first step is to add a DefaultProperty_DisplayName resource, just as you did for the WPF editor.

To add a resource

  1. In the Resources folder of the DetailControlExtension.Client.Design project, open the shortcut menu and choose Add New Item.

  2. In the Add New Item dialog box, expand the General node, and choose Resources file.

  3. In the Name field, enter DesignResources, and then click Add.

  4. In the DesignResources.resx file, add a string named “DefaultProperty_DisplayName”, and assign it a value of <Summary>.

    This resource string will be used by the converter when no value has been specified by the application developer.

Next, link to the three converters and the utility file that you created earlier, sharing the code into the DetailControlExtension.Client.Design project.

To share the converter code

  1. In Solution Explorer, open the shortcut menu for the DetailControl.Client.Design project, and then choose Add New Folder. Name the new folder Editors.

  2. Open the shortcut menu for the DetailControl.Client.Design node, and then choose Add Existing Item.

  3. Choose the Common project in your solution, and then choose the ModelUtilities file.

  4. Expand the drop-down list on the Add button, and then choose Add As Link

  5. Select the Editors node, open the shortcut menu, and then choose Add Existing Item.

  6. In the Add Existing Item dialog box, locate the Editors folder in the DetailControl.Client.Design project and then choose the DisplayNameConverter, GetTextPropertiesConverter, and PropertyNameToDisplayNameConverter files.

  7. Expand the drop-down list on the Add button, and then choose Add As Link.

Next, create a Silverlight version of the property editor. The implementation of the control resembles the WPF version. The difference is that the DataContext of this control is IPropertyEntry. Therefore, the binding paths are slightly different.

To create the Silverlight control

  1. In Solution Explorer, open the shortcut menu for the Editors node in the DetailControl.Client.Design project, and then choose Add New Item.

  2. In the Add New Item dialog box, expand the Silverlight node, and choose Silverlight User Control.

  3. In the Name field, enter ClientEntityPropertyPicker, and then click Add.

    An EntityPropertyPicker.xaml file and an EntityPropertyPicker.xaml.vb or EntityPropertyPicker.xaml.cs file will be added to the Editors folder.

  4. Replace the existing XAML with the following implementation for the control.

    <UserControl x:Class="DetailControlExtension.Editors.ClientEntityPropertyPicker"
        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"
        xmlns:e="clr-namespace:DetailControlExtension.Editors;assembly=DetailControlExtension.Client.Design"
        mc:Ignorable="d"
        >
        <UserControl.Resources>
            <e:PropertyNameToDisplayNameConverter x:Key="PropertyNameToDisplayNameConverter" />
            <e:DisplayNameConverter x:Key="DisplayNameConverter" />
            <e:GetTextPropertiesConverter x:Key="GetTextPropertiesConverter" />
        </UserControl.Resources>
    
        <Grid>
            <Grid.RowDefinitions>
                <RowDefinition Height="Auto" />
                <RowDefinition Height="Auto" />
            </Grid.RowDefinitions>
    
            <!-- That is the label to show the property name.  Use a converter to show a ':' behind the name. -->
            <TextBlock x:Name="EditorLabel" 
                       Text="{Binding Path=DisplayName, Converter={StaticResource DisplayNameConverter}}"/>
    
            <!-- 
                This is the ComboBox to pick up the value.  The DataContext of this control is an IPropertyEntry object.
                Use the 'GetTextPropertiesConverter' to collect all valid choices.  The PropertyValue.Value is the value of the DisplayProperty of the control.
                In SilverLight, ItemsSource needs to be set before SelectedItem to ensure that the control will work correctly.
            -->
            <ComboBox Margin="0,1,0,0" Grid.Row="1"
                      ItemsSource="{Binding Path=PropertyValue.ModelItem, Converter={StaticResource GetTextPropertiesConverter}}"
                      SelectedItem="{Binding Path=PropertyValue.Value, Mode=TwoWay}"
                      AutomationProperties.LabeledBy="{Binding ElementName=EditorLabel}"
                      HorizontalAlignment="Stretch"
                      >
                <ComboBox.ItemTemplate>
                    <DataTemplate>
                        <!-- The 'PropertyNameToDisplayNameConverter' is used to convert the empty string to a developer friendly string. -->
                        <TextBlock Text="{Binding Converter={StaticResource PropertyNameToDisplayNameConverter}}" />
                    </DataTemplate>
                </ComboBox.ItemTemplate>
            </ComboBox>
    
        </Grid>
    
    </UserControl>
    
  5. Close and save the ClientEntityPropertyPicker.xaml file.

  6. In the Editors folder, open the shortcut menu and choose Add New Item.

  7. In the Add New Item dialog box, expand the Code node, and choose Class.

  8. In the Name field, enter ClientEntityPropertyPickerEditor and then click Add.

  9. Add the following code to the class.

    No code example is currently available or this language may not be supported.

    Again, this code resembles the code you added in the WPF project.

  10. Expand the ClientEntityPropertyPicker.xaml node, open the ClientEntityPropertyPicker.xaml.vb or ClientEntityPropertyPicker.xaml.cs file, and replace the contents with the following.

    No code example is currently available or this language may not be supported.
  11. Close and save the ClientEntityPropertyPicker.xaml.vb or ClientEntityPropertyPicker.xaml.cs file.

This completes the property editor. You can now test the control and observe the updated behavior in the run-time screen designer.

When the DisplayProperty property of a control is a computed field, the control will display the old value of the property before the property is calculated. This is not the expected pattern for a LightSwitch control. LightSwitch controls typically indicate that a computed value is still being computed so that the end user does not make a decision based on an old value.

For value controls, this is usually handled directly by ContentItemPresenter hosting the control. Since there are multiple binding paths in a detail control, you have to handle the status by writing some code. To support this, you first have to add a new dependency property to the DetailControl.xaml.vb or DetailControl.xaml.cs file in the Client project.

To add a dependency property

  1. In Solution Explorer, in the Controls folder of the DetailControlExtension.Client project, open the DetailControl.xaml.vb or DetailControl.xaml.cs file.

  2. Add the following code to the DetailControl class:

    No code example is currently available or this language may not be supported.

    Next, add some code to the SetContentDataBinding method to bind the property.

  3. Add the following code to reset the binding at the beginning of the SetContentDataBinding()method.

    No code example is currently available or this language may not be supported.
  4. Next add the following code inside the body of the method just after the line that sets the EntityPropertyProperty.

    No code example is currently available or this language may not be supported.

Next, add a converter to help create the necessary UI.

To add a converter

  1. In Solution Explorer, open the shortcut menu for the Presentation/Controls folder in the DetailControl.Client project, and then choose Add New Item.

  2. In the Add New Item dialog box, expand the Code node, and choose Class.

  3. In the Name field, enter IsComputingVisibilityConverter, and then click Add.

  4. Add the following code to the IsComputingVisibilityConverter class.

    No code example is currently available or this language may not be supported.

Next, define the user interface in the DetailControl.xaml file.

To define the user interface

  1. In Solution Explorer, open the DetailControl.xaml file in the Client project.

  2. Add a namespace mapping, as follows.

    xmlns:ctl="clr-namespace:DetailControlExtension.Presentation.Controls;assembly=DetailControlExtension.Client"
    
  3. Add a resource dictionary element, as follows.

    <UserControl.Resources>
            <ResourceDictionary>
                <ctl:IsComputingVisibilityConverter x:Key="IsComputingVisibilityConverter" />
            </ResourceDictionary>
        </UserControl.Resources>
    
  4. Add a new element inside the ControlName.xaml file to show a “-” symbol when a bound computed property is being computed. This should be added inside the Grid element, after the TextBox element.

    <!-- 
            This is a sample to show an indicator to indicate a computed property is still being computed.  This overlaps the TextBox to prevent the user from making a decision based on an expired value.
            -->
            <Border BorderBrush="{StaticResource TextBoxBorderBrush}" 
                    Background="{StaticResource TextBoxBackgroundBrush}" 
                    HorizontalAlignment="Stretch"
                    VerticalAlignment="Stretch"
                    Visibility="{Binding IsComputed, Converter={StaticResource IsComputingVisibilityConverter}}">
                <TextBlock Foreground="{StaticResource TextBoxTextBrush}" Text="-" VerticalAlignment="Center" Margin="3,0" />
            </Border>
    

    While the property is being computed, the control will obtain the value from the DataBinding and set the visibility of the extra layer to Visible. That layer will overlay the original TextBox and show the computing state.

When a detail control is placed inside a list, the delete key is handled by the list control. In most cases, you will not want the contents of the detail control to be deleted. To prevent this deletion, you want your control to handle the KeyUp event.

To handle the KeyUp event

  1. In Solution Explorer, in the Controls folder of the DetailControlExtension.Client project, open the DetailControl.xaml file.

  2. Add the following code to the TextBox element, after the IsReadOnly line.

    KeyUp="DetailTextBox_KeyUp"
    
  3. Open the DetailControl.xaml.vb or DetailControl.xaml.cs file in the Controls folder of the DetailControlExtension.Client project.

  4. Add the following event handler.

    No code example is currently available or this language may not be supported.

LightSwitch supports enabling controls to behave differently when they are displayed in a DataGrid or similar container control. This may be required for performance—editable controls can be more expensive to display—and to achieve the correct tabbing and focus behavior. If a focusable control is displayed inside a DataGrid, a user will be able to set focus to both the DataGrid cell and also the underlying control when tabbing through the grid. This leads to two tab stops for each cell. It leads to additional problems when the DataGrid starts displaying cached row UI because of virtualization. The display mode of a control should therefore never be focusable or enable editing.

To prevent editing, implement a display mode version of your control which will not receive focus. You do not have to create another version of your control to do this; you only need disable the TabStop in the TextBox when your control is in display mode.

To handle keyboard navigation

  1. In Solution Explorer, in the Controls folder of the DetailControlExtension.Client project, open the DetailControl.xaml.vb or DetailControl.xaml.cs file.

  2. Add a new dependency property to the DetailControl class, as follows.

    No code example is currently available or this language may not be supported.
  3. Open the DetailControl.xaml file in the Controls folder of the DetailControlExtension.Client project.

  4. Add a binding to the TextBox element, just after the IsReadOnly line.

    IsTabStop="{Binding AllowsTabStop}"
    
  5. The next step is to create a display mode template for your control. The template should be added to the DetailControlFactory class that is generated by the control template in the DetailControl.xaml.vb or DetailControl.xaml.cs file in the Client project.

  6. Add a new private field as follows.

    No code example is currently available or this language may not be supported.
  7. Add a new constant string for the template, as follows.

    No code example is currently available or this language may not be supported.

    You can see that the constant string resembles the regular template; the only difference is that AllowsTabStop is set to False in this case.

  8. Replace the existing GetDisplayModeDataTemplate method with the following code.

    No code example is currently available or this language may not be supported.

    Next, implement a new ISupportTextInput interface in the DetailControl class to make sure that the DataGrid can forward input characters to your control when it switches a display mode cell to edit mode. Without it, input may be lost before the DataGrid sets up the edit control.

  9. Add the IsupportTextInput interface to the DetailControl class definition.

    Partial Public Class DetailControl
            Inherits UserControl
            Implements IContentVisual
            Implements ISupportTextInput
    
    public partial class DetailControl : UserControl, IContentVisual, ISupportTextInput
    
  10. Add the implementation for ISupportTextInput to the DetailControl class.

    No code example is currently available or this language may not be supported.

If you inspect the implementation of this class, you will see that it implements the regular DataTemplate and returns null in the GetDisplayModeDataTemplate method. If a display mode template is not defined, LightSwitch will always use the standard template.

This completes the detail control walkthrough; you should now have a fully functioning control extension that you can reuse in any LightSwitch project. This was just one example of a detail control; you might want to create a control that is significantly different in appearance or behavior. The same basic steps and principles apply to any detail control.

If you are going to distribute your control extension, there are a couple more steps to take. To make sure that the information displayed for your extension in the project designer and in Extension Manager are correct, update the properties for the VSIX package. For more information, see How to: Set VSIX Package Properties. In addition, there are several things to consider if you are going to distribute your extension publicly. For more information, see How to: Distribute a LightSwitch Extension.

Show: