Walkthrough: Creating a Business Type Extension

This walkthrough demonstrates how to create a business type extension for Visual Studio LightSwitch. Creating a business type extension in LightSwitch enables you to present data in a way that is most appropriate for your application without changing the data type in which the data is stored in the underlying database. For example, you can store values in the Decimal data type in the database but use the built-in Money business type to display those values in Currency format on the screen, providing a value control for displaying, editing, and validating currency.

For every business type extension, you must perform the following tasks:

  • Create an extension project.

  • Specify the data type to override.

The following tasks are optional:

  • Test the business type extension.

  • Define a corresponding control for the business type.

  • Define validation for the business type.

  • Add a custom property to the business type.

  • Build a property editor for business type properties.

Prerequisites

  • Visual Studio 2013 Professional

  • Visual Studio 2013 SDK

  • LightSwitch Extensibility Toolkit for Visual Studio 2013

Create a Business Type Extension Project

The first step is to create a project and add a LightSwitch Business Type 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 BusinessTypeExtension 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 BusinessTypeExtension.Lspkg project.

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

  3. In the Add New Item dialog box, choose Business Type.

  4. In the Name field, enter PositiveInteger 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.

Specify the Data Type to Override

A business type overrides a base data type, providing custom formatting and validation. You must update the metadata for the business type to reflect the underlying type to override. In addition to base data types, you can also override other business types. For a list of supported data types that you might want to override, see Supported Data Types for LightSwitch Extensions.

To specify the type

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

  2. Expand the Metadata and Types nodes, open the shortcut menu for the PositiveInteger.lsml file and choose Open With.

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

  4. In the SemanticType element, replace the UnderlyingType value with Int32, and replace the DisplayName value with Positive Integer, as follows.

    <SemanticType Name="PositiveInteger"
        UnderlyingType=":Int32">
        <SemanticType.Attributes>
          <DisplayName Value="Positive Integer" />
        </SemanticType.Attributes>
      </SemanticType>
    

    The UnderlyingType value tells LightSwitch that the data for the type should be stored as an Int32 data type in the database. Data types must be specified in an .lsml file by using the module name and the name of the data type. The ":" notation is a shortcut for "Microsoft.LightSwitch:". The DisplayName value will appear in the Type list in the Table Designer. Therefore, you want to provide a friendly name.

At this point you have created a business type, and you can test it in a LightSwitch application.

Test the Business Type Extension

You can test the business type extension 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

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

To test the business type

  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 BusinessTypeTest, and then choose the OK button to create a test project.

  5. In Solution Explorer, choose the BusinessTypeTest project.

  6. On the menu bar, choose Project, BusinessTypeTest Properties.

  7. In the project designer, on the Extensions tab, select the BusinessTypeExtension check box.

  8. Add a table with a Name field of type String, and a Score field of type Positive Integer.

  9. Add a List and Details screen with the table as the Screen Data source, and then in the screen designer, notice that the control for the Score field is a PositiveInteger Control.

  10. On the menu bar, choose Debug, Start Debugging. You can observe the behavior of the PositiveInteger Control control in the application by adding some data.

You may have noticed that the PositiveInteger Control control is a TextBox that only accepts numeric input. The behavior is the same as for a ShortInteger data type. In order to make your Positive Integer business type more useful, you can perform one or more of the optional tasks for creating a business type extension. These tasks are covered in the rest of the walkthrough.

Define a Corresponding Control

When you create a business type extension, a corresponding LightSwitch control is created. By default, a simple TextBox control is defined in Extensible Application Markup Language (XAML); you can modify or replace the XAML code to create a custom control for your business type.

Any control that you can use for the base type can also be used for the business type. Therefore, the included control is not required unless you want to change the behavior or the user interface.

You can also define multiple controls for a business type. For more information, see How to: Create Multiple Controls for a Business Type.

To replace the default control

  1. In Solution Explorer, choose the Presentation, Controls folder in the BusinessTypeExtension.Client project, and then open the PositiveIntegerControl.xaml file.

  2. Replace the <TextBox Text="{Binding StringValue, Mode=TwoWay}"/> element with the following XAML code.

    <Grid>
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="*"/>
                <ColumnDefinition Width="Auto"/>
            </Grid.ColumnDefinitions>
    
            <Slider Value="{Binding Details.Value, Mode=TwoWay}" Minimum="-100" Maximum="100" SmallChange="1" LargeChange="5"/>
            <TextBlock Grid.Column="1" Text="{Binding Value}" MinWidth="30"/>
        </Grid>
    

    The code illustrates how to replace the default TextBox control with a Slider control inside a Grid layout. It sets the fixed Minimum and Maximum values for the slider and also adds a TextBlock control to display the current value.

At this point you can test the behavior again in the experimental instance. When you run the BusinessTypeTest project, you can see that the TextBox control has been replaced by a Slider, and that the value is displayed in a TextBlock.

Define Validation for the Business Type

In most cases, you will want to add built-in validation for your business type. To add this functionality, you must add a validation rule for the type. You can easily manage the data in your built-in control. However, a developer might want to use a different control to display your business type. To enforce validation when the business type is used with any kind of control, you must implement validation on both the server and the client. Only a validation rule can satisfy those needs.

Before you add a validation rule, you must add a static class to define some constant names for the data type and control. You will use those names later in your validation rules.

To define constants

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

  2. On the Project menu, choose Add Class.

  3. In the Add New Item dialog box, choose the Name field, enter BusinessTypeExtensionModule, and then choose the Add button.

  4. In the BusinessTypeExtensionModule file, replace the existing contents with the following code:

    Friend Module BusinessTypeExtensionModule
    
        Private Const Name As String = "BusinessTypeExtension"
        Private Const ModulePrefix As String = Name + ":"
        Private Const AttributePrefix As String = ModulePrefix + "@"
    
        Friend Class PositiveInteger
    
            Private Const Name As String = "PositiveInteger"
            Public Const GlobalName As String = ModulePrefix + Name
    
            Friend Class ValidationAttribute
    
                Private Const Name As String = "PositiveIntegerValidation"
                Public Const GlobalName As String = AttributePrefix + Name
            End Class
    
        End Class
    
    End Module
    
    namespace BusinessTypeExtension
    {
        internal static class BusinessTypeExtensionModule
        {
            private const string Name = "BusinessTypeExtension";
            private const string ModulePrefix = Name + ":";
            private const string AttributePrefix = ModulePrefix + "@";
    
            internal static class PositiveInteger
            {
                private const string Name = "PositiveInteger";
                public const string GlobalName = ModulePrefix + Name;
    
                internal static class ValidationAttribute
                {
                    private const string Name = "PositiveIntegerValidation";
                    public const string GlobalName = AttributePrefix + Name;
                }
            }
        }
    }
    

The next step is to add validation rules. The validation code that you add to the Common project will be loaded in both the LightSwitchLightSwitch client and the server. This action makes sure that the same logic will be enforced on both tiers.

To add a validation rule

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

  2. On the Project menu, choose Add Class.

  3. In the Add New Item dialog box, choose the Name field, type PositiveIntegerValidation, and then choose the Add button.

  4. In the PositiveIntegerValidation file, replace the existing contents with the following code.

    Imports System
    Imports System.Collections.Generic
    Imports System.ComponentModel.Composition
    Imports System.Linq
    Imports Microsoft.LightSwitch
    Imports Microsoft.LightSwitch.Model
    Imports Microsoft.LightSwitch.Runtime.Rules
    
    Public Class PositiveIntegerValidation
        Implements IAttachedPropertyValidation
    
        Public Sub New(attributes As IEnumerable(Of IAttribute))
            Me.attributes = attributes
        End Sub
    
        Private attributes As IEnumerable(Of IAttribute)
    
        Public Sub Validate(value As Object, results As IPropertyValidationResultsBuilder) Implements Microsoft.LightSwitch.Runtime.Rules.IAttachedPropertyValidation.Validate
            If value IsNot Nothing Then
    
                ' Ensure the value type is integer.
                If GetType(Integer) IsNot value.GetType() Then
                    Throw New InvalidOperationException("Unsupported data type.")
                End If
    
                Dim intValue As Integer = DirectCast(value, Integer)
    
                ' First validation rule: value should be greater than 0.
                If intValue <= 0 Then
                    results.AddPropertyError("Value should be greater than 0")
                End If
    
            End If
        End Sub
    
    End Class
    
    using System;
    using System.Collections.Generic;
    using System.ComponentModel.Composition;
    using System.Linq;
    using Microsoft.LightSwitch;
    using Microsoft.LightSwitch.Model;
    using Microsoft.LightSwitch.Runtime.Rules;
    
    namespace BusinessTypeExtension
    {
        public class PositiveIntegerValidation : IAttachedPropertyValidation
        {
            public PositiveIntegerValidation(IEnumerable<IAttribute> attributes)
            {
                _attributes = attributes;
            }
    
            private IEnumerable<IAttribute> _attributes;
    
            public void Validate(object value, IPropertyValidationResultsBuilder results)
            {
                if (null != value)
                {
                    // Ensure the value type is integer.
                    if (typeof(Int32) != value.GetType())
                    {
                        throw new InvalidOperationException("Unsupported data type.");
                    }
    
                    int intValue = (int)value;
    
                    // First validation rule: value should be greater than 0.
                    if (intValue <= 0)
                    {
                        results.AddPropertyError("Value should be greater than 0");
                    }
                }
            }
        }
    }
    

    In this code, a validator for the business type implements the interface IAttachedPropertyValidation, which defines a function that is named Validate. This function will be called any time that the business type must be validated, and you can get the data value from the parameter value and add the validation result back to the parameter results.

    Next you must define a validation attribute that can be used in the validator.

To define a validation attribute

  1. In Solution Explorer, choose the BusinessTypeExtension.Common project, expand the Metadata, Types nodes, and then open the shortcut menu for the PositiveInteger.lsml file and choose Open With.

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

  3. Add the following code to the SemanticType.Attributes element, just after the <DisplayName Value="Positive Integer" /> line.

    <Attribute Class="@PositiveIntegerValidation">
         </Attribute>
    
  4. Add the following code after the SemanticType element.

    <AttributeClass Name="PositiveIntegerValidation">
    
        <AttributeClass.Attributes>
          <Validator />
          <SupportedType Type="PositiveInteger?" />
        </AttributeClass.Attributes>
          </AttributeClass>
    

    Next you must define a ValidatorFactory.

To create a ValidatorFactory

  1. In Solution Explorer, choose the BusinessTypeExtension.Common project, and then open the PositiveIntegerValidation file.

  2. Add the following code under the PositiveIntegerValidation class to implement the PositiveIntegerValidationFactory class.

    <Export(GetType(IValidationCodeFactory))>
    <ValidationCodeFactory(BusinessTypeExtensionModule.PositiveInteger.ValidationAttribute.GlobalName)>
    Public Class PositiveIntegerValidationFactory
        Implements IValidationCodeFactory
    
        Public Function Create(modelItem As Microsoft.LightSwitch.Model.IStructuralItem, attributes As System.Collections.Generic.IEnumerable(Of Microsoft.LightSwitch.Model.IAttribute)) As Microsoft.LightSwitch.Runtime.Rules.IAttachedValidation Implements Microsoft.LightSwitch.Runtime.Rules.IValidationCodeFactory.Create
    
            If Not IsValid(modelItem) Then
                Throw New InvalidOperationException("Unsupported data type.")
            End If
    
            Return New PositiveIntegerValidation(attributes)
    
        End Function
    
        Public Function IsValid(modelItem As Microsoft.LightSwitch.Model.IStructuralItem) As Boolean Implements Microsoft.LightSwitch.Runtime.Rules.IValidationCodeFactory.IsValid
    
            Dim nullableType As INullableType = TryCast(modelItem, INullableType)
    
            ' Get underlying type if it is a INullableType.
            modelItem = If(nullableType IsNot Nothing, nullableType.UnderlyingType, modelItem)
    
            ' Ensure that type is a positive integer semantic type, or that a type inherits from it.
            While TypeOf modelItem Is ISemanticType
                If String.Equals(DirectCast(modelItem, ISemanticType).Id, BusinessTypeExtensionModule.PositiveInteger.GlobalName, StringComparison.Ordinal) Then
                    Return True
                End If
                modelItem = DirectCast(modelItem, ISemanticType).UnderlyingType
            End While
    
    
            ' If the conditions aren't met, LightSwitch will not display the validation rule for
            '   this model item.
            Return False
    
        End Function
    
    End Class
    
    [Export(typeof(IValidationCodeFactory))]
        [ValidationCodeFactory(BusinessTypeExtensionModule.PositiveInteger.ValidationAttribute.GlobalName)]
        public class PositiveIntegerValidatorFactory : IValidationCodeFactory
        {
            public IAttachedValidation Create(IStructuralItem modelItem, IEnumerable<IAttribute> attributes)
            {
                // Enusre that the type model item is a positive integer semantic type (or nullable positive integer)
                if (!IsValid(modelItem)).
                {
                    throw new InvalidOperationException("Unsupported data type.");
                }
    
                return new PositiveIntegerValidator(attributes);
            }
    
            public bool IsValid(IStructuralItem modelItem)
            {
                INullableType nullableType = modelItem as INullableType;
    
                // Get underlying type if it is an INullableType.
                modelItem = null != nullableType ? nullableType.UnderlyingType : modelItem;
    
                // Ensure that type is a positive integer semantic type, or that a type inherits from it.
                while (modelItem is ISemanticType)
                {
                    if (String.Equals(((ISemanticType)modelItem).Id, BusinessTypeExtensionModule.PositiveInteger.GlobalName, StringComparison.Ordinal))
                    {
                        return true;
                    }
                    modelItem = ((ISemanticType)modelItem).UnderlyingType;
                }
    
                // If the conditions aren't met, LightSwitch will not display the validation rule for
                //   this model item.
                return false;
            }
        }
    

    Next, you must add a validation indicator for the control to provide feedback if the user specifies a value that is not valid. Although you could create your own validation indicator for the control, the preferred behavior is to use the built-in validation indicator.

To add a validation indicator

  1. In Solution Explorer, choose the BusinessTypeExtension.Client project, and then open the PositiveIntegerControl.xaml file.

  2. Add the following namespace reference.

    xmlns:slu="clr-namespace:Microsoft.LightSwitch.Utilities.SilverlightUtilities;assembly=Microsoft.LightSwitch.Client"
    
  3. Add the following XAML code following the reference line that you just added.

    ToolTipService.ToolTip="{Binding Description}"
    

    Although it is not required, this enables the runtime ToolTip for the control, displaying the Description property value supplied by the application developer.

  4. Add the following XAML code inside the <Grid> element, under the <TextBlock Grid.Column="1" Text="{Binding Value}" MinWidth="30"/> line:

    <!-- Our data context is an IContentItem instance.  Bind to its StringValue property.  It will 
                 automatically handle Silverlight conversion errors and other behavior. -->
            <slu:ValidatableContentControl ValidationDataSource="{Binding StringValue}" Grid.ColumnSpan="2"/>
    

    This provides the same automatic validation as the built-in LightSwitchLightSwitch controls.

At this point, you can test the behavior again in the experimental instance. When you run the BusinessTypeTest project, set the Score value to a negative number and observe the validation behavior.

Add a Custom Property to the Business Type

Built-in validation rules for a business type are always enforced. You might also want to add validation that the application developer can turn on or off. You can accomplish this by adding a validation property that will appear in the Properties window when the business type is selected in the Table Designer.

The first step is to update the metadata with the value or values the application developer wants in the data model.

To update the metadata

  1. In Solution Explorer, choose the BusinessTypeExtension.Common project, expand the Metadata, Types node, and then open the shortcut menu for the PositiveInteger.lsml file and choose Open With.

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

  3. Add the following code to the AttributeClass element, just under the AttributeClass.Attributes element.

    <AttributeProperty Name="ShouldBeEven" MetaType="Boolean">
          <AttributeProperty.Attributes>
            <Category Value="Validation" />
            <DisplayName Value="Should be an even number" />
            <UIEditor Id="CheckBoxEditor"/>
          </AttributeProperty.Attributes>
        </AttributeProperty>
    

    A Boolean ShouldBeEven property is added to the validation metadata so that the application developer can set the property value in the Table Designer’s property sheet. Note that CheckBoxEditor is specified for the UIEditor attribute of the property, which is appropriate for a Boolean property. Without it, LightSwitchLightSwitch will automatically choose a property editor for the validation property in the designer property sheet. You can find the names of all built-in property editors inside the class Microsoft.LightSwitch.Designers.PropertyPages.UI.CommonPropertyValueEditorNames.

  4. Update the SemanticType definition by adding a Property element inside the Attribute element, as follows.

    <Attribute Class="@PositiveIntegerValidation">
            <Property Name="ShouldBeEven" Value="False"/>
          </Attribute>
    

    This code specifies that if a value is not chosen, the default value is False.

Next you must add some constants to represent the property.

To add constants

  1. In Solution Explorer, choose the BusinessTypeExtension.Common project, and then open the BusinessTypeExtensionModule.vb or BusinessTypeExtensionModule.cs file.

  2. Add the following code to the ValidationAttribute class, just after the Public Const GlobalName As String = AttributePrefix + Name (VB) or public const string GlobalName = AttributePrefix + Name; (C#) line.

    Public Const ShouldBeEvenPropertyName As String = "ShouldBeEven"
                Public Const ShouldBeEvenPropertyEditorName = GlobalName + ShouldBeEvenPropertyName + "Editor"
    
    public const string ShouldBeEvenPropertyName = "ShouldBeEven";
                    public const string ShouldBeEvenPropertyEditorName = GlobalName + ShouldBeEvenPropertyName + "Editor";
    

    This provides constants for the property and the property editor.

Next you will add validation code for the new property.

To add validation code

  1. In Solution Explorer, choose the BusinessTypeExtension.Common project, and then open the PositiveIntegerValidation.vb or PositiveIntegerValidation.cs file.

  2. Replace the existing Validate method with the following code.

    Public Sub Validate(value As Object, results As IPropertyValidationResultsBuilder) Implements Microsoft.LightSwitch.Runtime.Rules.IAttachedPropertyValidation.Validate
            If value IsNot Nothing Then
    
                ' Ensure that the value type is integer.
                If GetType(Integer) IsNot value.GetType() Then
                    Throw New InvalidOperationException("Unsupported data type.")
                End If
    
                Dim intValue As Integer = DirectCast(value, Integer)
    
                ' First validation rule: value should be greater than 0.
                If intValue <= 0 Then
                    results.AddPropertyError("Value should be greater than 0")
                End If
    
                ' Second validation rule: Value should be an even number.
                '  It's a dynamic rule - application developers set a property to specify whether 
                '    they want to enable it or not.
                Dim validationAttribute As IAttribute = Me.attributes.FirstOrDefault()
                If validationAttribute IsNot Nothing AndAlso
                    validationAttribute.Class IsNot Nothing AndAlso
                    validationAttribute.Class.Id = BusinessTypeExtensionModule.PositiveInteger.ValidationAttribute.GlobalName AndAlso
                    DirectCast(validationAttribute(BusinessTypeExtensionModule.PositiveInteger.ValidationAttribute.ShouldBeEvenPropertyName), Boolean) AndAlso
                    Not intValue Mod 2 = 0 Then
    
                    results.AddPropertyResult("Value should be an even number", ValidationSeverity.Error)
    
                End If
    
            End If
        End Sub
    
    public void Validate(object value, IPropertyValidationResultsBuilder results)
            {
                if (null != value)
                {
                    // Ensure that the value type is integer.
                    if (typeof(Int32) != value.GetType())
                    {
                        throw new InvalidOperationException("Unsupported data type.");
                    }
    
                    int intValue = (int)value;
    
                    // First validation rule: value should be greater than 0.
                    if (intValue <= 0)
                    {
                        results.AddPropertyError("Value should be greater than 0");
                    }
    
                    // Second validation rule: Value should be an even number.
                    // It's a dynamic rule - application developers set a property to specify whether 
                    //   they want to enable it or not.
                    IAttribute validationAttribute = _attributes.FirstOrDefault();
                    if (validationAttribute != null &&
                        validationAttribute.Class != null &&
                        validationAttribute.Class.Id == BusinessTypeExtensionModule.PositiveInteger.ValidationAttribute.GlobalName &&
                        (bool)validationAttribute[BusinessTypeExtensionModule.PositiveInteger.ValidationAttribute.ShouldBeEvenPropertyName] &&
                        intValue % 2 != 0)
                    {
                        results.AddPropertyResult("Value should be an even number", ValidationSeverity.Error);
                    }
                }
            }
    

    This code adds a second validation rule that raises a validation error if the value is not an even number.

At this point you can test the behavior again in the experimental instance. In the BusinessTypeTest project, open the Table Designer and select the Score field. Notice that the Should be an even number property now appears in the Validation section of the Properties window. Change the property value and observe the behavior in the running application.

Build a Property Editor for Business Type Properties

If the built-in property editor does not satisfy your needs, you can build your own property editor. For example, the PhoneNumber business type provides an external editor for setting phone number formats.

In this example, you build a Windows Presentation Foundation (WPF) property editor that displays a dialog box for the Should be an even number property. Unlike control property editors, you only need to implement a design-time version of the editor. Business type properties are not available in the runtime screen designer.

The first step is to add some references.

To add references

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

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

    The typical location of this file is in the C:\Program Files (x86)\MSBuild\Microsoft\VisualStudio\LightSwitch\v4.0 folder.

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

  4. Browse to the BusinessTypeExtension.Common project in your solution, and then choose BusinessTypeExtensionModule.vb or BusinessTypeExtensionModule.cs.

  5. On the Add button drop-down list, choose Add As Link.

    This allows the Design project to share the constants that you have already defined.

The next step is to create the WPF dialog box that will be used to edit the validation property.

To create a WPF dialog box

  1. In Solution Explorer, open the shortcut menu for the BusinessTypeExtension.Design project, and then choose Add New Item.

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

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

  4. Open the ShouldBeEvenEditorDialog.xaml.vb or ShouldBeEvenEditorDialog.xaml.cs code-behind file, and replace the contents with the following code:

    Imports System.Windows
    
    Public Class ShouldBeEvenEditorDialog
        Inherits Window
        Public Property Value As Nullable(Of Boolean)
            Get
                Return MyBase.GetValue(ShouldBeEvenEditorDialog.ValueProperty)
            End Get
            Set(value As Nullable(Of Boolean))
                MyBase.SetValue(ShouldBeEvenEditorDialog.ValueProperty, value)
            End Set
        End Property
    
        Public Shared ReadOnly ValueProperty As DependencyProperty =
            DependencyProperty.Register(
                "Value",
                GetType(Nullable(Of Boolean)),
                GetType(ShouldBeEvenEditorDialog),
                New UIPropertyMetadata(False))
    
    End Class
    
    using System.Windows;
    
    namespace BusinessTypeExtension
    {
        /// <summary>
        /// Interaction logic for EditorDialog.xaml
        /// </summary>
        public partial class ShouldBeEvenEditorDialog : Window
        {
            public ShouldBeEvenEditorDialog()
            {
                InitializeComponent();
            }
    
            public bool? Value
            {
                get { return (bool?)GetValue(ValueProperty); }
                set { SetValue(ValueProperty, value); }
            }
            public static readonly DependencyProperty ValueProperty =
                DependencyProperty.Register("Value", typeof(bool?), typeof(ShouldBeEvenEditorDialog), new UIPropertyMetadata(false));
        }
    }
    
  5. Open the ShouldBeEvenEditorDialog.xaml file, and then replace the contents with the following code:

    <Window x:Class="BusinessTypeExtension.ShouldBeEvenEditorDialog"
            xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml"
            WindowStartupLocation="CenterOwner"
            ShowInTaskbar="False"
            ResizeMode="NoResize"
            Title="Business Type Property Editor" Height="200" Width="300">
        <Grid>
            <CheckBox
                IsChecked="{Binding Value, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}, Mode=TwoWay}"
                Content="Should be even number"
                HorizontalAlignment="Center"
                VerticalAlignment="Center"
                />
        </Grid>
    </Window>
    

    This code defines a dialog that contains a CheckBox control for the property.

The next step is to add a WPF resource dictionary to the BusinessTypeExtension.Design project to contain the implementation.

You cannot plug the dialog box into the LightSwitch property sheet directly. You must display a link label on the property sheet and show the dialog box when the application developer clicks the link.

The LightSwitch design time requires that you implement an IPropertyValueEditorProvider, which provides a DataTemplate that is used to create a control that is hosted inside the property sheet. The DataContext of the control will be set to be an instance of IBindablePropertyEntry, which the control uses to access the value, name, and other information about the property.

To add a resource dictionary

  1. In Solution Explorer, open the context menu for the BusinessTypeExtension.Design project, and then choose Add New Item.

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

  3. In the Name field, enter EditorTemplates, and then choose the Add button.

  4. Replace the existing code with the following code:

    <ResourceDictionary
        xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:self="clr-namespace:BusinessTypeExtension">
    
        <DataTemplate x:Key="ShouldBeEvenEditorTemplate">
            <Label>
                <Hyperlink
                    Command="{Binding EditorContext}"
                    ToolTip="{Binding Entry.Description}">
                    <Run
                        Text="{Binding Entry.DisplayName, Mode=OneWay}"
                        FontFamily="{DynamicResource DesignTimeFontFamily}"
                        FontSize="{DynamicResource DesignTimeFontSize}"
                        />
                </Hyperlink>
            </Label>
        </DataTemplate>
    
    </ResourceDictionary>
    

    This provides the hyperlink that will be displayed in the property sheet for the Table Designer.

    Note

    At this point you will see an error in the XAML. You can ignore this for now; you will add the referenced namespace later.

The next step is to create the property editor control, which is straightforward. The key is to have a hyperlink and bind it to the EditorContext, which must be a WPF ICommand object that the editor provides. Next, you must create an editor to expose this control to the LightSwitch designer, and you must also implement the command in the editor.

To create the property editor control

  1. In Solution Explorer, open the shortcut menu for the BusinessTypeExtension.Design project, and then choose Add New Item.

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

  3. In the Name field, enter ShouldBeEvenEditor, and then choose the Add button.

  4. Replace the contents with the following code.

    Imports System
    Imports System.ComponentModel.Composition
    Imports System.Runtime.InteropServices
    Imports System.Windows
    Imports System.Windows.Input
    Imports System.Windows.Interop
    Imports Microsoft.LightSwitch.Designers.PropertyPages
    Imports Microsoft.LightSwitch.Designers.PropertyPages.UI
    
    Friend Class ShouldBeEvenEditor
        Implements IPropertyValueEditor
    
        Public Sub New(entry As IPropertyEntry)
            Me.command = New EditCommand(entry)
        End Sub
    
        Private command As ICommand
    
        Public ReadOnly Property Context As Object Implements Microsoft.LightSwitch.Designers.PropertyPages.UI.IPropertyValueEditor.Context
            Get
                Return Me.command
            End Get
        End Property
    
        Public Function GetEditorTemplate(entry As Microsoft.LightSwitch.Designers.PropertyPages.IPropertyEntry) As System.Windows.DataTemplate Implements Microsoft.LightSwitch.Designers.PropertyPages.UI.IPropertyValueEditor.GetEditorTemplate
            Dim dict As ResourceDictionary = New ResourceDictionary()
            dict.Source = New Uri("BusinessTypeExtension.Design;component/EditorTemplates.xaml", UriKind.Relative)
            Return dict("ShouldBeEvenEditorTemplate")
        End Function
    
        Private Class EditCommand
            Implements ICommand
    
            Public Sub New(entry As IPropertyEntry)
                Me.entry = entry
            End Sub
    
            Private entry As IPropertyEntry
    
            Public Function CanExecute(parameter As Object) As Boolean Implements System.Windows.Input.ICommand.CanExecute
                Return True
            End Function
    
            Public Event CanExecuteChanged(sender As Object, e As System.EventArgs) Implements System.Windows.Input.ICommand.CanExecuteChanged
    
            Public Sub Execute(parameter As Object) Implements System.Windows.Input.ICommand.Execute
    
                Dim dialog As ShouldBeEvenEditorDialog = New ShouldBeEvenEditorDialog()
                dialog.Value = Me.entry.PropertyValue.Value
    
                ' Set the parent window of your dialog box to the IDE window; this ensures the win32 window stack works correctly.
                Dim wih As WindowInteropHelper = New WindowInteropHelper(dialog)
                wih.Owner = GetActiveWindow()
    
                dialog.ShowDialog()
    
                Me.entry.PropertyValue.Value = dialog.Value
    
            End Sub
    
            ''' <summary>
            ''' GetActiveWindow is a Win32 method; we import the method to get the IDE window
            ''' </summary>
            Declare Function GetActiveWindow Lib "User32" () As IntPtr
    
        End Class
    
    End Class
    
    <Export(GetType(IPropertyValueEditorProvider))>
    <PropertyValueEditorName(BusinessTypeExtensionModule.PositiveInteger.ValidationAttribute.ShouldBeEvenPropertyEditorName)>
    <PropertyValueEditorType("System.Boolean")>
    Friend Class ShouldBeEventEditorProvider
        Implements IPropertyValueEditorProvider
    
        Public Function GetEditor(entry As Microsoft.LightSwitch.Designers.PropertyPages.IPropertyEntry) As Microsoft.LightSwitch.Designers.PropertyPages.UI.IPropertyValueEditor Implements Microsoft.LightSwitch.Designers.PropertyPages.UI.IPropertyValueEditorProvider.GetEditor
            Return New ShouldBeEvenEditor(entry)
        End Function
    
    End Class
    
    using System;
    using System.ComponentModel.Composition;
    using System.Runtime.InteropServices;
    using System.Windows;
    using System.Windows.Input;
    using System.Windows.Interop;
    using Microsoft.LightSwitch.Designers.PropertyPages;
    using Microsoft.LightSwitch.Designers.PropertyPages.UI;
    
    namespace BusinessTypeExtension
    {
    
    internal class ShouldBeEvenEditor : IPropertyValueEditor
        {
            public ShouldBeEvenEditor(IPropertyEntry entry)
            {
                _editCommand = new EditCommand(entry);
            }
            private ICommand _editCommand;
    
            public object Context
            {
                get { return _editCommand; }
            }
    
            public DataTemplate GetEditorTemplate(IPropertyEntry entry)
            {
                ResourceDictionary dict = new ResourceDictionary() { Source = new Uri("BusinessTypeExtension.Design;component/EditorTemplates.xaml", UriKind.Relative) };
                return (DataTemplate)dict["ShouldBeEvenEditorTemplate"];
            }
    
            private class EditCommand : ICommand
            {
                public EditCommand(IPropertyEntry entry)
                {
                    _entry = entry;
                }
    
                private IPropertyEntry _entry;
    
                #region ICommand Members
    
                bool ICommand.CanExecute(object parameter)
                {
                    return true;
                }
    
                public event EventHandler CanExecuteChanged { add { } remove { } }
    
                void ICommand.Execute(object parameter)
                {
                    ShouldBeEvenEditorDialog dialog = new ShouldBeEvenEditorDialog() { Value = (bool?)_entry.PropertyValue.Value };
    
                    // Set the parent window of your dialog box to the IDE window; this ensures that the win32 window stack works correctly.
                    WindowInteropHelper wih = new WindowInteropHelper(dialog);
                    wih.Owner = GetActiveWindow();
    
                    dialog.ShowDialog();
    
                    _entry.PropertyValue.Value = dialog.Value;
                }
    
                #endregion
    
                /// <summary>
                /// GetActiveWindow is a Win32 method; we import the method to get the IDE window.
                /// </summary>
                [DllImport("user32")]
                public static extern IntPtr GetActiveWindow();
            }
        }
    
    
        [Export(typeof(IPropertyValueEditorProvider))]
        [PropertyValueEditorName(BusinessTypeExtensionModule.PositiveInteger.ValidationAttribute.ShouldBeEvenPropertyEditorName)]
        [PropertyValueEditorType("System.Boolean")]
        internal class ShouldBeEvenEditorProvider : IPropertyValueEditorProvider
        {
            public IPropertyValueEditor GetEditor(IPropertyEntry entry)
            {
                return new ShouldBeEvenEditor(entry);
            }
        }
    }
    

    The code in this example exposes a component that is named with a PropertyValueEditorName attribute. A LightSwitch designer will load this component when the designer must show a property that has its UIEditor set to the same string as the value of the PropertyValueEditorName attribute.

    The LightSwitch designer will create an instance of IPropertyValueEditor from the factory and get both the DataTemplate and the Context object, which is optional. The designer uses the DataTemplate to create the UI control and hosts it on the property sheet. The control can access the context object through an EditorContext property on its DataContext object.

    The editor provides a special context object, which is actually an ICommand object. The UI control binds to ICommand. Therefore, it will be executed when the application developer clicks the link. The dialog box appears and commits the value inside the command object.

  5. On the menu bar, choose Build, Build BusinessTypeExtension.Design.

    Building will resolve the missing namespace error in the EditorTemplates.xaml file.

As the final step, you must hook up the property editor by updating the metadata for the business type.

To hook up the property editor

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

  2. Expand the MetaData and Types nodes, open the shortcut menu for the PositiveInteger.lsml file and choose Open With.

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

  4. Replace the element <UIEditor Id="CheckBoxEditor"/> in the AttributeProperty element with the following code.

    <!--<UIEditor Id="CheckBoxEditor"/>-->
            <Description Value="Modify this property in a dialog" />
            <UIEditor Id="BusinessTypeExtension:@PositiveIntegerValidationShouldBeEvenEditor" />
    

    This tells LightSwitch to use the dialog box to display the property.

    The PositiveInteger business type is now finished, and you can test it again in the experimental instance. Select the Score field in the table editor and notice that a hyperlink for the Should be an even number property is now displayed in the Properties window. Click the hyperlink to display the custom editor.

Next Steps

This concludes the business type walkthrough; you should now have a fully functioning business type extension that you can reuse in any LightSwitch project. This was just one example of a business type; you might want to create a business type that is significantly different in behavior or that uses a different control. The same basic steps and principles apply to any business type extension, but there are other concepts that apply in other situations. For example, you might want the control for your business type to support read-only data or support editing inside a DataGrid control. For more information, see Additional LightSwitch Control Concepts.

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

See Also

Tasks

How to: Create Multiple Controls for a Business Type

How to: Create a LightSwitch Control

How to: Distribute a LightSwitch Extension

How to: Set VSIX Package Properties

Concepts

Additional LightSwitch Control Concepts

Defining, Overriding, and Using LightSwitch Control Properties

LightSwitch Extensibility Toolkit for Visual Studio 2013