CHAPTER 20

image

Creating Data and Presentation Extensions

In the previous chapter, I introduced the topic of extension projects and showed you how to build control extensions. In this chapter, I’ll describe the remaining extension types that are available with the Extensibility Toolkit. Specifically, I’ll show you how to do the following:

  • create business type and data-source extensions
  • create screen template extensions
  • create shell and theme extensions

At the start of this chapter, I’ll show you how to build a business type to store time-duration data. This business type will include custom validation and a custom control. Developers can configure this validation rule through the properties sheet in Visual Studio’s table designer. I’ll show you how to extend Visual Studio to support this functionality.

Next, I’ll describe how to customize the look and feel of your applications by building shell and theme extensions. After this section, I’ll show you how to build desktop and HTML client screen template extensions. By building screen template extensions, you can automate the repetitive steps that you often carry out when you develop screens. In the final section of this chapter, I’ll demonstrate how to build a data-source extension that you can use to read data from the Windows event log.

Introduction

This chapter relies on content that I covered in Chapter 19. So, to begin, here’s a summary of the most relevant points that apply to this chapter.

At the time of writing, Microsoft had not released a version of the Extensibility Toolkit for Visual Studio 2015. Therefore, the content in this chapter relies on Visual Studio 2013 and the Extensibility Toolkit for Visual Studio 2013. A prerequisite of this chapter is that you install both of these components.

Once you install the Extensibility Toolkit, you can add new Extensibility Solutions through Visual Studio’s “File image New” menu item. An Extensibility Solution consists of seven separate projects. A notable project is the LSPKG project, because this is the project that you use to add new extension items to your solution.

Another important project is the VSIX project. This project produces the output that you use to install your extension into Visual Studio. To build extensions that work with LightSwitch 2015, you must modify the Compatible Versions setting in the manifest file to include versions 14 or above of Visual Studio.

With these points in mind, let’s now move on and look at the first extension type in this chapter: the business type extension.

Creating a Business Type Extension

Business types are special types that extend the basic LightSwitch data types. The business types that ship with LightSwitch include phone number, money, and web address. The benefit of a business type is that it encapsulates both validation and custom data entry controls. By building a business type extension, you can easily reuse the validation and custom control logic in multiple projects, and you can also easily share it other developers.

To demonstrate, I’ll show you how to build a business type for time durations. This example reuses the duration editor control and includes additional validation. If a developer creates a table and adds a field that uses the duration type, the validation allows the developer to specify the maximum duration that users can enter into the field, expressed in days. Here’s an overview of the steps to build this example:

  1. Create a new business type and set the underlying data type.
  2. Create and/or associate custom controls with your business type.
  3. Create an attribute that stores the maximum acceptable duration (for validation purposes).
  4. Write the validation logic in the Common project.

To create a business type, right-click your LSPKG project, select “New Item,” and add a new business type called DurationType. When you do this, the business type template does two things. First, it creates an LSML file that you can use to define your business type, and, second, it creates a new custom control for your business type.

Our duration type uses the integer data type as its underlying storage type. This is defined in your business type’s LSML file, which you can find in the Common project. To define the underlying storage type, open the DurationType.lsml file in the Common project and modify the UnderlyingType setting, as shown in Listing 20-1 image.

The UnderlyingType values that you can specify include :Int32, :String, and :Double. Appendix B shows a list of acceptable values that you can use. The DisplayName property image specifies the name that identifies your business type in the table designer. Toward the bottom of the LSML file, the DefaultViewMapping image element enables you to specify the default control for your business type. By default, the template sets this to the custom control that it automatically creates. So, in this case, it sets it to DurationTypeControl.

Although there’s still much more functionality that you can add, you’ve now completed the minimum steps that are needed for a functional business type. If you want to, you can compile and install your extension.

Associating Custom Controls with Business Types

LightSwitch associates business types with custom controls. For instance, if you’re in the screen designer and add a property that’s based on the phone number business type, LightSwitch gives you the choice of using the phone number editor, phone number viewer, textbox, or label controls.

Strictly speaking, you don’t configure a business type to work with specific set of controls. The relationship actually works in the other direction—you define custom controls to work with business types by adding data to the custom control’s metadata.

When you create a business type, the template generates an associated control in your Client project’s Controls folder (for example, ApressExtensionVB.ClientPresentationControlsDurationTypeControl.xaml). This automatically provides you with a custom control that accompanies your business type.

Because you already created a duration control, you can save yourself some time by associating it with your business type. The association between custom controls and business types is defined in your custom control’s LSML file. To associate the duration editor control (discussed in Chapter 19) with your duration business type, open the LSML file for your control. You can find this file in the Metadata > Controls folder of your Common project. Find the Control.SupportedDataTypes node, add a SupportedDataType element, and set the DataType attribute to DurationType (as shown in Listing 20-2). DurationType refers to the name of your business type, as defined in the LSML file of your business type.

The code in Listing 20-2 configures the duration editor control to support integer and duration data types. You can add additional SupportedDataType entries here if you want your duration editor Control to support extra data types.

Enforcing Validation

The benefit of business-type validation is that LightSwitch applies your validation logic, irrespective of the control that you use on your screen. For desktop applications, business-type validation runs on both the client and server. Therefore, business-type validation code belongs in the Common project. In this section, I’ll show you how to create a validation rule that allows developers to specify the maximum duration in days that users can enter.

You can allow developers to control the behavior of your business type by creating attributes that LightSwitch shows in the table designer. Custom attributes are defined in a business type’s LSML file, and you’ll now create an attribute that allows developers to specify the maximum duration that an instance of your business type can store. Open the LSML file for your business type and add the parts that are shown in Listing 20-3.

The code in Listing 20-3 defines two things. It associates validation logic with your business type, and it defines an attribute that allows developers to specify the maximum duration that an instance of your business type can store. Let’s now look at this code in more detail.

Associating Validation Logic with a Business Type

To associate your business type with validation logic, there are two tasks that you need to carry out in your LSML file. The first is to define an AttributeClass, and the second is to apply the class to your business type.

The code in Listing 20-3 defines an AttributeClass image that includes the Validator attribute image. It includes an attribute that defines the data type that the validation applies to image, and the value that you specify here should match the name of your business type. After you define your AttributeClass, you need to define an Attribute for your business type image.

When you write this code, there are two important naming rules you must adhere to:

  • Your AttributeClass name image must match your Attribute’s Class value image.
  • Your Attribute’s Class value image must be preceded with @ symbol.

If you don’t abide by these naming conventions, your validation simply won’t work. You’ll notice that the name of the class is MaxIntegerValidationId, and by the end of this section, you’ll notice that you don’t write any .NET code that creates an instance of the MaxIntegerValidationId class. Technically, MaxIntegerValidationId is a class that exists in the model’s conceptual space rather than in the .NET code space. In practice, it’s easier to think of MaxIntegerValidationId as a string identifier, and for this reason, this example names the class with an Id suffix to allow you to more easily follow its usage in the proceeding code files.

Defining Property Sheet Attributes

The code in Listing 20-3 defines an attribute that controls the maximum duration that an instance of your business type can store. To define an attribute, you need to add an AttributeProperty image to your AttributeClass. Once you do this, you can define additional attributes image that control the way that your attribute appears in the table designer. These attributes include

  • DisplayName: Defines the label text that appears next to your property
  • Category: Specifies the group that your property appears in
  • UIEditor: Defines the editor that allows users to modify your property value. Chapter 12 contains a list of valid UIEditor choices.

Once you’ve defined your AttributeProperty, you’ll also need to define a Property that’s associated with your business type’s Attribute. This Property specifies the default value that your business type applies if the developer hasn’t set a value through the properties sheet image.

Figure 20-1 illustrates how this property appears in the table designer at runtime.

9781484207673_Fig20-01.jpg

Figure 20-1. Maximum Days attribute, as shown in the properties sheet

Applying Validation Logic

Now that you’ve set up your LSML file to support custom validation, let’s take a closer look at how business-type validation works. In the LSML for your business type, you used the identifier MaxIntegerValidationId. This identifier ties your business type with a validation factory. When LightSwitch needs to validate the value of a business type, it uses this identifier to determine the factory class that it should use. The factory class then returns an instance of a validation class that contains the validation logic for your business type. In our example, we named our validation class MaxIntegerValidation. This class implements the IAttachedPropertyValidation interface, and it implements a method called Validate. This is the method that LightSwitch calls to validate the value of your business type. Figure 20-2 illustrates this process.

9781484207673_Fig20-02.jpg

Figure 20-2. Applying validation logic

To apply this validation to your business type, let’s create the factory and validation classes. Create a new class file in your Common project and name it MaxIntegerValidationFactory. Now, add the code that’s shown in Listing 20-4.

The first part of this code contains the identifier (AttributeClass) that links your validation factory to your LSML file image. The syntax that you use is very important. You need to prefix the identifier with the namespace of your project, followed by the : symbol and the @ symbol.

The remaining code in the validation factory performs validation to ensure that the data type of the model item matches your business type, and it returns a new instance of your MaxIntegerValidation class if the test succeeds. In the code that carries out this test, it’s important to specify your business-type identifier image in the correct format. It should contain the namespace of your project, followed by the : symbol, followed by the business type name.

At this point, you’ll see compiler errors, because the MaxIntegerValidation class doesn’t exist. The next step is to create a new class in your Common project, name it MaxIntegerValidation, and add the code that’s shown in Listing 20-5.

The code in Listing 20-5 defines a class that implements the Validate method image. LightSwitch calls this method each time it validates the value of your business type. You can use this method to retrieve the data value from the value parameter, apply your validation rules, and return any errors through the results parameter. The validation code retrieves the MaxDays attribute by querying the collection of attributes image that are supplied to the validation class by the factory. The code retrieves the first attribute and checks that it relates to your duration type. In the test that the code carries out, notice the syntax of the class ID image. It should contain the namespace of your project, followed by the : symbol, followed by the @ symbol, followed by the validation identifier that you specify in your LSML file.

Creating Custom Property Editor Windows

By default, the MaxDay attribute that you added to your business type shows up as a textbox in the table designer’s property sheet. In this section, you’ll learn how to develop a popup window for developers to edit custom attribute values. The phone number business type provides a great example of a business type that works just like this. As you’ll recall from Chapter 2 (Figure 2-8), this business type allows developers to define phone number formats through a popup Phone Number Formats dialog. The advantage of a popup window is that it gives you more space and allows you to create a richer editor control that can contain extra validation or other neat custom features. In this section, you’ll find out how to create an editor window that allows developers to edit the MaxDay attribute. The custom editor window will allow developers to set the value by using a slider control.

Chapter 19 showed you how to customize the screen designer’s properties sheet, and this example works in a similar fashion. When you’re in the table designer and Visual Studio builds the property sheet for an instance of your business type, it uses the LSML metadata to work out what custom attributes there are and what editor control it should use.

The LSML file allows you to specify a UIEditor for each custom attribute in your business type. The value that you specify provides an identifier that associates the attribute with a factory class. When Visual Studio builds the property sheet, it uses this identifier to find a matching factory class. It then uses the factory class to return an editor class that implements the IPropertyValueEditor interface. The editor class implements a method called GetEditorTemplate that returns the XAML that plugs into Visual Studio’s property sheet. To create a custom popup property editor, you’d return a piece of XAML that contains a hyperlink control that opens your custom property editor in a new window. Figure 20-3 illustrates this process.

9781484207673_Fig20-03.jpg

Figure 20-3. Creating a popup custom editor window

You’ll remember from Chapter 19 that Visual Studio is built with WPF; therefore, creating a popup window that integrates with the IDE will involve writing a custom WPF user control. Just like the other examples in this book that extend the Visual Studio IDE, you’ll carry out this work in your Design project.

To create the custom editor window, right-click your Design project, click “Add image New Item,” and select “User Control (WPF).” Once your control opens in Visual Studio, modify the XAML as shown in Listing 20-6.

This code defines the XAML for the window that opens from the properties sheet. Figure 20-4 shows how this window looks at runtime. The XAML contains a TextBlock that shows the selected Maximum Days value image. The Slider control image allows the developer to edit the value, and because of the data-binding code that you’ve added, the TextBlock value updates itself when the developer changes the value through the slider control.

9781484207673_Fig20-04.jpg

Figure 20-4. The popup window that allows users to set maximum days

Image Caution  If you copy and paste the XAML in a WPF user control, your code might fail to compile due to a missing InitializeComponent method. See here for more details:

http://stackoverflow.com/questions/954861/why-can-visual-studio-not-find-my-wpfinitializecomponent-method

Once you’ve added your WPF user control, add the code that’s shown in Listing 20-7.

The code in Listing 20-7 configures your control to inherit from the Window class image. By default, your WPF control inherits from the UserControl class. This code contains a dependency property called ValueProperty, which is of data type integer image. This property enables the window to share the selected value with the code in the properties sheet that opens the window.

Image Caution  When you create dependency properties, make sure to pass the correct value to the UIPropertyMetadata constructor. For example, if you define a decimal dependency property and you want to set the default value to 0, use the syntax UIPropertyMetadata (0d). If you set default values that don’t match the data type of your dependency property, you’ll receive an obscure error at runtime that can be difficult to decipher.

You’ve now created the window that allows the developer to edit the MaxDays attribute, and the next step is to define the UI that contains the hyperlink that opens this window. Create a new user control (WFP) called EditorTemplates.xaml in your Design project and add the code that’s shown in Listing 20-8.

The code in Listing 20-8 defines a Resource Dictionary that contains a single data template called MaxIntegerEditorTemplate image. A Resource Dictionary gives you the ability to add additional templates at a later point in time. Make sure to configure the xmlns:self value so that it points to the namespace of your extension project image. When you add a WPF user control, the template creates a .NET code file that corresponds with your XAML file. Because this code isn’t necessary, you can simply delete this file.

The next step is to create your editor class. Create a new class called MaxIntegerEditor in your Design project and add the code that’s shown in Listing 20-9.

Listing 20-9 defines the factory class that specifies the PropertyValueEditorName attribute image. The value of this attribute identifies your custom editor control, and this is the value that you specify in your business type’s LSML file to link an attribute to a custom editor.

When the LightSwitch table designer needs to display a custom editor, it uses the factory class to create an instance of a MaxIntegerEditor object image. The table designer calls the GetEditorTemplate image method to retrieve the UI control to display on the property sheet. This UI control binds to an ICommand object, and the UI control can access this object through the EditorContext property. This UI control shows a hyperlink on the properties sheet, and when the developer clicks the on the link, it calls the code in the Execute method image, which opens the dialog. This allows the developer to set the MaxDays attribute, and once the developer enters the value, the code sets the underlying property value using the value that was supplied through the dialog.

The next step is to link your custom editor class to your custom attribute. To do this, open the LSML file for your business type, find the section that defines the UIEditor, and modify it as shown in Listing 20-10.

Listing 20-10 shows the snippet of XML that links the custom editor to the MaxDays attribute. The important thing to note here is to make sure that the UIEditor value image matches the PropertyValueEditorName value that you set in Listing 20-9.

Using Your Business Type

The duration business type is now complete, and you’re ready to build and use it. To demonstrate how to use the business type that you created, open the Timesheet table and select the DurationMins property. In the table designer, you can change the data type from Integer to Duration, and when you open the properties sheet, you can click the Maximum Days link to open the dialog that contains the slider control (Figure 20-5).

9781484207673_Fig20-05.jpg

Figure 20-5. The new slider control that appears in the properties sheet

Figure 20-6 shows a screen at runtime. The Maximum Days setting for the DurationMins property is set to two days, and the screenshot shows the error message that appears when you try to enter a duration that’s greater than two days. Notice how the control type for DurationMins is set to a textbox rather than the default duration editor control. This emphasizes that LightSwitch applies your business-type validation, irrespective of the control type that you choose.

9781484207673_Fig20-06.jpg

Figure 20-6. Business-type validation at runtime

Creating a Custom Shell Extension

By creating a LightSwitch shell, you can radically change the appearance of your application. A custom shell allows you to change the position of where the command menu, navigation items, and screens appear.

When you add a new shell, the template creates a blank canvas that allows you to add as little or as much as you like. If, for some reason, you don’t want to include a navigation menu, that’s no problem—you can simply choose not to implement that functionality. Some developers have created bare shells and used custom controls to achieve an appearance that looks nothing like a LightSwitch application. Custom shells, therefore, allow you to carry out extreme modification to your application’s UI.

A custom shell is a Silverlight concept, so the work that you’ll carry out takes place in your Client project. A LightSwitch shell consists of a XAML file that defines the layout and UI elements of your shell. Data binding then allows you to connect your UI elements with your LightSwitch application through special view models that LightSwitch provides.

In this section, I’ll show you how to build a custom shell. To demonstrate how to modify the behavior of your shell, I’ll show you how to create a navigation system that uses drop-down boxes. The overview of how to create a custom shell involves the following:

  1. Create a new shell and set the name and description.
  2. Write the XAML that defines your shell’s UI and data binds to LightSwitch’s view models.
  3. Write the underlying .NET code that supports your shell.

Preparing Your Project

Just as you would with all extension types, you would create a new shell by right-clicking your LSPKG project, choosing the “Add image New” option, and selecting “Shell” in the Add New Item dialog. To carry out the example that’s shown in this section, create a new shell called ApressShell. Once you do this, the template creates the following two files:

  • ClientPresentationShellsApressShell.xaml This file contains the markup that defines the presentation and UI elements for your shell.
  • ClientPresentationShellsComponentsApressShell.vb This .NET code file contains the implementation code that allows your shell to work with MEF (Managed Extensibility Framework), and includes properties that identify your shell.

A large part of the shell development process involves rewriting parts of LightSwitch that you probably take for granted. A new shell provides you with a UI that’s completely blank, and in this section I’ll show you how to recreate the tab controls that allow users to switch screens. This functionality relies on a couple of DLLs that you need to add to your Client project. These DLLs and their default location on a 64-bit computer are shown here:

  • System.Windows.Controls.dll

    C:Program Files (x86)Microsoft SDKsSilverlightv5.0Libraries

  • Microsoft.LightSwitch.ExportProvider.dll

    C:Program Files (x86) Microsoft SDKsLightSwitchv5.0Client

Defining the Look of Your Shell

The key part of shell design is to work out how you want your shell to look. The shell that we’ll build in this example stacks the UI elements from top to bottom. Figure 20-7 shows the proposed layout of this shell.

9781484207673_Fig20-07.jpg

Figure 20-7. The proposed layout for your shell

The code in Listing 20-11 contains the XAML that achieves the look that’s shown in Figure 20-7. Take a look at this code, but don’t add it to your ApressShell.xaml file yet. It won’t compile because it depends on some components that you haven’t yet defined.

You can use the comments in Listing 20-11 to match the code blocks with the screen sections that are shown in Figure 20-7. The first part of this code image defines several supporting resources. This includes the value converters to support the shell’s functionality and a template that defines the tab headings that appear above each screen. The tab heading includes the screen name and elements to indicate whether the screen is dirty or contains validation errors.

The next part of the XAML image defines the parent StackPanel that arranges the contents of your shell in a top-to-bottom manner. The first control inside the StackPanel displays the application logo image, and the next control is a listbox control image that binds to the screen commands in your application. The standard commands that LightSwitch shows on each screen include “Save” and “Refresh.” The next section contains a pair of combo box controls image that you’ll customize to allow users to navigate your application. The final part of the XAML contains the tab control image that contains the screen area and a TextBlock image that displays the currently logged-in user. In the sections of this chapter that follow, I’ll refer back to this XAML and describe the code that’s shown in further detail.

When you add this XAML to your project, make sure to set the two namespace references that are indicated in the name of your project image.

Binding Data to Your Shell

LightSwitch exposes shell-related data through six view models, which are shown in Table 20-1.

Table 20-1. Shell View Models

Name

View Model ID

Description

Navigation

NavigationViewModel

Provides access to navigation groups and screens

Commands

CommandsViewModel

Provides access to the commands that are normally shown in the command section

Active Screens

ActiveScreensViewModel

Provides access to the active screens in your application (that is, the screens that your user has opened)

Current User

CurrentUserViewModel

Provides information about the current logged-on user

Logo

LogoViewModel

Provides access to the image that’s specified in the application’s logo property

Screen Validation

ValidationViewModel

Provides access to the validation information

LightSwitch provides a Component View Model Service that allows you to bind UI elements to view models by simply adding one line of code against the control that you want to data bind. The Component View Model Service uses MEF to find and instantiate a view model and to set it as the data context for the specified control. The advantage is that it makes it really simple for you to consume the data from the view models. To demonstrate how this works, let’s take a closer look at the XAML that shows the screen commands (Listing 20-12).

Listing 20-12 is a simplified version of the code from Listing 20-11 that highlights the parts that are specific to data binding. The first section of this code defines a listbox control. If you’re not familiar with Silverlight, it’s useful to understand that this control isn’t only just designed to display simple lists of text data. It allows you to bind to a data source and to render each data item using rich data controls that can include images and other Silverlight controls. The initial part of listbox control defines the “parent” container for your list items. This code renders the child items horizontally by defining an ItemsPanelTemplate element that contains a StackPanel with its Orientation set to Horizontal image.

The definition of the listbox control uses the Component View Model Service to bind it to the commands view model. It does this by applying the following line of code:

image

The Component View Model Service requires you to supply a view model name. The name that you provide should begin with Default, followed by the view model ID that’s shown in Table 20-1. The code that’s shown in image sets the data context of your listbox to the commands view model. The commands view model exposes the individual commands through a collection called ShellCommands, and the next line of code data binds the ItemsSource of the listbox control to this collection image.

The DataTemplate section presents each data item in your listbox as a button. Each button is bound to an IShellCommand object, and you can use the properties of this object to control the enabled status image, image image, and display name image of each command item.

When a user clicks on one of these buttons, LightSwitch won’t automatically execute the command. You’ll need to write custom code that executes the command, which you’ll find out how to do shortly.

Displaying Your Application’s Logo

The top section of your shell displays the logo that’s defined in the properties of the LightSwitch application. You can show the application logo by using the Component View Model Service to bind an image control to the logo view model.

Listing 20-13 illustrates the code that you would use and provides another example of how to use the Component View Model Service.

Adding Code That Supports Our Custom Shell

Up till now, I’ve shown you plenty of XAML that defines the appearance of your shell. Although the big advantage of custom shells is that they allow you to carry out extreme UI customization, the side effect is that you need to implement a lot of the functionality that you would take for granted in LightSwitch. This includes writing the code that executes command items, manages screens, and enables navigation. To support the XAML that you’ve created so far, you’ll need to add the following code to your shell:

  • ApressShell.xaml.vb or ApressShell.xaml.cs

    This defines the .NET code behind your XAML file and contains the logic that enables your custom shell to open and close screens and respond to user-initiated actions.

  • ScreenWrapper class

    LightSwitch’s screen object doesn’t contain any properties that allow you to determine whether a screen is dirty or contains validation errors. This object extends LightSwitch’s IScreenObject and provides these extra functions.

  • Value Converters

    The shell that you’ve created includes UI elements that indicate whether the screen is dirty or contains validation errors. These value converters help to convert the property results from your screen object into types that your UI controls can consume.

Let’s begin by creating your ScreenWrapper class. Add a new class in your Client project’s Presentation > Shells folder and call it ScreenWrapper. Now, add the code that’s shown in Listing 20-14.

When you create a custom shell, it’s important to be able to access screens in code, and the ScreenWrapper object allows you to do this. It provides a thin wrapper around the IScreenObject object and exposes properties you use to determine if a screen is dirty or contains validation errors. The code in Listing 20-14 includes the following features:

  • 1 - Change Notification

    This class implements the INotifiedPropertyChanged interface and the PropertyChanged event. This allows the ScreenWrapper object to raise a notification if the underlying data becomes dirty or invalid. Ultimately, this allows you to build a UI that shows an indication as soon as a user makes a change or enters invalid data.

  • 2 - Exposes an IsDirty Property

    This class allows you to determine whether the user has made any data changes by providing an IsDirty property. This returns true if the screen contains changes.

  • 3 - Exposes a ValidationResults Property

    This class exposes a public property called ValidationResults that allows you to access any underlying validation errors.

  • 4 - Implements Underlying Screen Properties

    The class implements the underlying properties of IScreenObject and allows you to access the screen’s name, display name, and description in code. It also exposes methods such as Save and Refresh, which allow you to call these methods in code.

Once you add the ScreenWrapper class, the next step is to create the helper class that contains the value converters. To do this, add a new class in your Client project’s Presentation > Shells folder and call it ShellHelper. Now, modify your code, as shown in Listing 20-15.

This code defines four value converters; you’ll notice references to these converters in the XAML code that’s shown in Listing 20-11. The following list describes how these value converters apply to the XAML:

  • WorkspaceDirtyConverter

    The template for the screen tab title includes a text block that shows the value “*”; this symbol indicates to the user that the screen is dirty. The Visibility property of this text block is data-bound to the ScreenWrapper object’s IsDirty property. The WorkspaceDirtyConverter converts the Boolean IsDirty property to the visibility values of either Visible or Collapsed. Setting the visibility of a Silverlight control to Collapsed hides the control completely. The other visibility value that you can you set is Hidden. The difference between Hidden and Collapsed is that Hidden hides the control, but displays white space in its place instead.

  • ScreenHasErrorsConverter

    The screen tab title includes a text block that shows the value “!”; this symbol indicates to the user that the screen contains validation errors. The Visibility property of this text block is data-bound to the ScreenWrapper object’s ValidationResults.HasErrors property. The ScreenHasErrorsConverter converts the Boolean result to a visibility value of either Visible or Collapsed.

  • ScreenResultsConverter

    The tooltip property of the screen tab is bound to the ScreenWrapper’s ValidationResults property. This allows the user to hover his or her mouse over a screen tab to see a summary of any validation errors. ValidationResults returns a collection of errors, and the purpose of ScreenResultsConverter is to combine the individual error messages into a single string.

  • CurrentUserConverter

    The text block in the bottom part of the shell displays the name of the currently logged-on user. The data context of this control is the CurrentUserViewModel, and the text block binds to the CurrentUserDisplayName property. If authentication isn’t enabled in the LightSwitch application, CurrentUserConverter returns a string that indicates this condition, which is friendlier than showing nothing at all.

Now, compile your project so that the value converter code becomes available. You can now add the XAML for your shell, which was shown in Listing 20-11, and you can also add the .NET code-behind that’s shown in Listing 20-16.

Although Listing 20-16 contains a lot of code, you can split the logic into three distinct sections:

  • Section 1 – screen-handling code
  • Section 2 – command button-handling code
  • Section 3 – screen-navigation code

In the sections that follow, I’ll explain this code in more detail.

Managing Screens

The LightSwitch API provides an object called INavigationScreen. This object represents a screen-navigation item and provides a method that you can call to open the screen that it represents. The navigation view model gives you access to a collection of INavigationScreen objects, and, in general, you would bind this collection to a control that allows the user to select a screen. INavigationScreen provides an executable object that you can call to open a screen. But when you call this method, LightSwitch doesn’t show the screen to the user. The runtime simply “marks” the screen as open, and you’ll need to carry out the work that shows the screen UI to the user.

To work with screens, you’ll need to use an object that implements the IServiceProxy interface. This allows you to set up notifications that alert you whenever the runtime opens a screen, and you can use these notifications to add the code that shows the screen UI to the user. In Listing 20-16, the shell’s constructor uses the IServiceProxy to subscribe to the ScreenOpened, ScreenClosed, and ScreenRefreshed notifications. The code that you’ll find in “Section 1 – Screen-Handling Code” defines the following methods:

  • OnScreenOpened: Your shell calls this method when the runtime opens a screen. It creates a tab item and sets the contents of the tab to the UI of the newly opened screen. Screen objects expose their UI contents via a property called RootUI. The code creates a ScreenWrapper object from the underlying IScreenObject object and sets the data context of the tab item to the ScreenWrapper object.
  • OnScreenClosed: This method executes when the runtime closes a screen and removes it from the application’s collection of active screens. This custom method removes the tab item that displayed this screen, sets the selected tab to be the last tab in the tab control, and sets the current screen to the screen that’s contained in that tab.
  • OnScreenRefreshed: When a user refreshes a screen, the runtime actually creates a new IScreenObject and discards the old one. This screen replaces the data context of the tab item that contains the screen with a ScreenWrapper object that represents the new IScreenObject instance.
  • OnTabItemSelectionChanged: This method handles the SelectionChanged event of the tab control. The XAML for the tab control (Listing 20-11) defines OnTabItemSelectionChanged as the method that handles the SelectionChanged event. When a user switches tabs, this code uses the proxy to set the active screen. It’s important that you do this, because it causes the commands view model to update itself to reflect the commands of the new screen.

Executing Commands

The custom shell includes a command bar section that renders your screen commands using buttons. Typically, every screen includes save and refresh commands, in addition to any other commands that the developer might add through the screen designer.

Technically, the shell implements the command bar section through a list control that data binds to the commands view model (Listing 20-11). The list control’s data template defines a button control that represents each command data item.

The data context of each button control is an object that implements the IShellCommand interface. The IShellCommand object exposes a member called ExecutableObject. This object represents the logic that’s associated with the command item.

So, to make the buttons on your command bar work, you need to handle the Click event of the button, retrieve the IShellCommand object that’s bound to the button, and call the IShellCommand object’s ExecutableObject’s ExecuteAsync method. This code is shown in the GeneralCommandHandler method, in Listing 20-16 (Section 2 — Command Button-Handling Code).

Performing Navigation

Your custom shell includes a pair of combo boxes that allow your users to navigate your application. The first combo box displays a list of navigation groups. When the user selects a navigation group, the second combo box populates itself with a list of screens that belong to the selected navigation group.

The first navigation group combo box binds to the navigation view model’s NavigationItems collection. When a user selects a navigation group using the first combo box, the shell runs the code in the navigationGroup_SelectionChanged method and sets the data source of the second combo box to the Children collection of the NavigationGroup. This binds the second combo box to a collection of INavigationScreen objects.

When the user selects an item from the second combo box, the shell executes the navigationItems_SelectionChanged method. The code in this method retrieves the INavigationScreen object that’s bound to the selected item in the combo box. Just like the IShellCommand object, the INavigationScreen object exposes an ExecutableObject. The code then calls the ExecutableObject.ExecuteAsync method. This causes the runtime to open the screen and triggers the code that’s defined in your OnScreenOpened method. The code in this method creates a new screen tab and carries out the remaining actions that are needed to show the screen UI to the user. Figure 20-8 illustrates this process.

9781484207673_Fig20-08.jpg

Figure 20-8. Custom navigation process

Persisting User Settings

The IServiceProxy object includes a user-settings service that allows you to persist user settings, such as the positions of screen elements. For example, if you create a shell with a splitter control that allows users to apportion the visible screen area between the navigation and screen areas, you can save the screen sizing details when the user closes your application and restore the settings when your user next opens your application.

To demonstrate the user-settings service, we’ll add a feature to the shell that allows the user to hide the “logged-in user” section if authentication isn’t enabled in the application. When the user closes an application that doesn’t have authentication enabled, you’ll display a confirmation dialog that allows the user to permanently hide the “logged-in user” section. Listing 20-17 shows the code that adds this feature.

The code in the constructor adds an event handler for the user-settings service’s Closing event image. LightSwitch raises this event when the user closes your application. The user-settings service exposes two methods: GetSetting and SetSetting. The SetSetting method allows you to persist a setting by supplying a name/value pair as shown in image. When your application loads, the code in the constructor hides the LoggedInUser text block if the value of the HideLoggedInUser setting is true image.

Setting the Name and Description

The name and description for your shell are defined in the LSML file for your shell. Developers can view these details through the properties window of their LightSwitch solution. To set these details, modify the DisplayName and Description attributes, as shown in Listing 20-18.

Using Your Custom Shell

Your custom shell is now complete, and you can now build and share it with other developers. Once a developer installs your extension, LightSwitch adds your custom shell to the list of available shells that it shows in the properties window of each application. A developer can apply your shell by selecting it from the list. Figure 20-9 shows the final appearance of your screen.

9781484207673_Fig20-09.jpg

Figure 20-9. Illustration of the final shell

Creating a Custom Theme Extension

Custom themes are the ideal companion to a custom shell. You can use themes to customize your application’s font, color, and control styles. You’ll be pleased to know that it’s much simpler to create a theme than it is to create some of the extension types that you’ve already worked on. The process involves the following steps:

  1. Creating a new theme and setting the name and description.
  2. Modifying the style information that’s defined in the theme’s XAML file.

To begin, right-click your LSPKG project, select “New Item,” and create a new theme called ApressTheme. As soon as you do this, the template creates an XAML file in your Client project and opens it in Visual Studio’s XML text editor.

The template prepopulates this file with default fonts and colors. It groups the style definitions into well-commented sections. At this point, you could build and deploy your theme. But before you do this, let’s modify the fonts and colors that your theme applies.

Applying a Different Font

The default theme that the template creates uses the Segoe UI font, and you’ll find references to this font throughout your theme file. Figure 20-10 shows a screenshot of the theme file in Visual Studio’s editor. Notice how the file defines font styles, and notice how it sets the FontFamily value to Segoe UI, Arial. This setting defines Segoe UI as the preferred font and Arial as the fallback font. This means that LightSwitch will apply the Arial font only if the Segoe UI font isn’t available on the end-user PC.

9781484207673_Fig20-10.jpg

Figure 20-10. The contents of your theme file in Visual Studio

To change the font, simply replace the references to “Segoe UI” with the name of the font that you want to use. For example, you could replace “Segoe UI” with “Times New Roman,” “Tahoma,” or “Verdana.” To perform a global change, you can use Visual Studio’s “Find and Replace” option. You can find a full list of font name values that you can use through the following page on MSDN:

http://msdn.microsoft.com/en-us/library/cc189010(v=vs.95).aspx

Setting Different Colors

Changing the colors in a style is just as easy. For example, refer back to the command bar control that you added in the custom shell section. Listing 20-19 shows the code that defines the listbox control that contains the command buttons. You’ll notice that this code applies the static resource RibbonBackgroundBrush to the listbox control’s Background property. RibbonBackgroundBrush defines a key that links a shell with a theme.

If you now use the find feature in Visual Studio to search for the string RibbonBackgroundBrush in your theme file, you’ll find an entry that relates to this style. To apply a different background style, you can modify this entry as appropriate. To demonstrate this, we’ll apply a diagonal gradient background style that goes from white to black. Modify the RibbonBackgroundBrush style in your theme as shown in Listing 20-20.

Listing 20-20 defines the color codes that allow you to replicate this example. But rather than manually hand-code the styles in your theme, you can select a style entry and use the graphical designer that you’ll find in the properties sheet to define your colors and gradient styles (Figure 20-11).

9781484207673_Fig20-11.jpg

Figure 20-11. The contents of your theme file in Visual Studio

To complete your theme, the final step is to specify a name and description. The LSML file for your theme allows you to define these settings. You’ll find this file in your Common project, in the following location:

...CommonMetadataThemesApressTheme.lsml

Once you build and install your theme extension, you can apply it to your LightSwitch application through the properties windows for your application.

Figure 20-12 shows the command bar section of an application that applies the ApressTheme and highlights the white-to-black gradient style that runs from the top left to bottom right.

9781484207673_Fig20-12.jpg

Figure 20-12. Applying a gradient background to the command bar

Creating a Screen Template Extension

If you frequently create screens with common patterns, features, and code snippets, you can save yourself time by creating a custom screen template. In this section, I’ll show you how to build screen extensions for desktop and HTML client applications.

To explain this technique, I’ll demonstrate how to incorporate two of the screen designs from this book into screen template extensions. For the desktop client applications, I’ll create a template that builds a combined Add/Edit screen. And for HTML client applications, I’ll show you how to create a template that builds a View screen that features a Delete button and confirmation popup.

The manual process to build both of these screens involves several detailed steps. Therefore, the benefit of a screen template is that it automates these repetitive tasks and can save you time in the long run. The steps to build desktop and HTML screen templates are very similar. To begin, I’ll demonstrate the desktop client screen template, which involves the following steps:

  1. Add a screen template item and specify the template properties.
  2. Write code that generates the templated screen controls.
  3. Write code that generates the templated .NET code.

To add a new screen template item to an extension solution, right-click your LSPKG project, select “New Item,” and choose the “Screen Template” option. For this example, name your screen template AddEditScreenTemplate. Once you add a new screen template, Visual Studio opens the code file for your template in the code editor.

Setting Template Properties

You can set the properties of your template by changing the code that appears in the first section of the screen template’s code file. These properties include the name, description, and display name of your template. The screen template exposes many of these properties through Visual Studio’s Add New Screen dialog. Figure 20-13 illustrates this dialog and highlights the places where the Add New Screen dialog applies the screen template properties.

9781484207673_Fig20-13.jpg

Figure 20-13. Add New Screen dialog

Listing 20-21 highlights the places in the code file where you can set these properties.

The DisplayName property image defines the text that appears on the left-hand side of the Add New Screen dialog. When a developer selects a screen template, the dialog uses the value of the Description property image to set the text that appears in the center of the dialog. It also sets the value of the Screen Text textbox by using the StringNameFormat property image. In this example, the StringNameFormat property value includes a substitution character. This enables the Add New Screen dialog to prepend the data-source name to the beginning of the text that it shows in the Screen Text textbox.

The value of the SupportsChildCollections property image controls the visibility of the “Additional Data to Include” checkboxes. The RootDataSourceType image property defines the content that appears in the screen data drop-down. I’ll describe this in greater detail in the next section. In addition to these properties, there are two additional properties you can use to set the image and icon of your screen template.

Defining the Data-Source Type

When a developer selects a screen template from the Add New Screen dialog, the dialog populates the screen data drop-down with data that depends on the selected template. When you build a screen template, you can define the data that appears in the screen data drop-down by setting the RootDataSource property. Table 20-2 shows the four values that you can set for this property.

Table 20-2. Query Types

Name

Description

Collection

Allows the developer to select collections or multiple result queries

ScalarEntity

Allows the developer to select a single entity type or queries that return one item

NewEntity

Used for screens that are intended to create new entities

None

Used for screens in which no data needs to be selected

In our example template, set the RootDataSource property to ScalarEntity. This configures the screen data drop-down to show singleton queries when a developer selects your screen template.

Generating Screen Controls

The first and most important part of screen template design is to work out the screen components that you want to include in your template. The best way to do this is to create a normal screen and to use that to establish your requirements. Figure 20-14 shows the Add/Edit screen from Chapter 9 and highlights the steps to build this screen in the designer.

9781484207673_Fig20-14.jpg

Figure 20-14. Screen requirements

Once you establish the items that you want to add to your template, you need to translate these tasks into code. The screen template produces screens by calling a method called Generate. Listing 20-22 shows the code that produces the screen that’s shown in Figure 20-14.

The list below highlights the actions you would carry out in the screen designer if you were to create the screen manually, and it identifies the corresponding code in Listing 20-22 that carries out the same task.

  • Creating a Details screen and query

    The Add/Edit screen is based on a query that returns a single record by ID value. The good news is that you don’t need to write any specific code to create the screen’s underlying query. The screen template automatically creates a query when you set the RootDataSource to ScalarEntity. You can use the syntax host.PrimaryDataSourceProperty image to access the query that LightSwitch generates. If you set the RootDataSource to NewEntity instead, the PrimaryDataSourceProperty object would be a property that matches the data type the developer chooses in the Add New Screen dialog. And if you set the RootDataSource to Collection, the PrimaryDataSource would be a collection of entities (that is, a visual collection).

  • Adding group layouts

    Let’s imagine that you select a node in the screen designer's tree view and click the “Add” drop-down button. The code equivalent that performs this action is the AddContentItem method. This method requires you to supply the content item type that you want to add along with the name you want to give your new data item. The code in Listing 20-22 adds a new group layout by specifying the content item kind of ContentItemKind.Group image.

  • Adding a local property that matches the underlying query’s data type

    To add a new local property in the screen designer, you open the Add Data Item dialog and choose the radio option to add a local property. To carry out the same action in code, you would call the AddScreenProperty method image. Just like the Add Data Item dialog, you need to supply the name and data type of your new property. The code uses the PrimaryDataSourceProperty object to determine the data type of the underlying screen object and names the property after the entity type name, but with the word “Property” appended to the end. Another useful method is the AddScreenMethod method. This is the code equivalent of creating a screen method through the Add Data Item button.

  • Making the query parameter optional

    To enable your screen to work in New Data entry mode, you must set the ID parameter on your screen to be optional. You might expect to find a Boolean property you can use to set the optional status of a parameter to false, but interestingly, no such property exists. So, to make a parameter optional, you need to carry out a step that may seem unusual. To make a parameter optional, you simply change the underlying data type from an integer to a nullable integer, as shown in image.

  • Deleting the screen properties that are bound to the query

    When you create an Add/Edit screen manually through the screen designer, you need to delete the data items that bind to the underlying query. You don’t need to carry out this action when you create a screen template in code. The screen that the screen generator creates includes only the root element and doesn’t include any extra content that you would need to delete.

  • Adding screen properties that bind to your local entity property

    When you create an Add/Edit screen manually through the screen designer, the final step is to drag your local property onto your screen and to add data items that bind to your local property’s data items. The AddContentItem method image allows you to add the local property onto your screen in code. If you carried out this task in the screen designer, LightSwitch would render your local property as an auto-complete box. In code, the AddContentItem method does the same thing—it’ll add the local screen property as an auto-complete box. To change the auto-complete box to a rows layout in code, you would call the SetContentItemView method. This method accepts a reference to the content item and a ViewID that identifies a rows layout image.

A common design task is to set the control type for a data item, and, therefore, the SetContentItemView method is a very useful method. However, the difficulty with this method is that you set the control type with a ViewID and it’s not obvious what ViewID you need to use. To help you, I’ve included a list of ViewIDs you can use in Appendix C.

Another useful method you can call is the ExpandContentItem method. This method expands a content item by adding child items that represent each property in the entity. This method mimics the use of the Reset button in the LightSwitch screen designer.

Generating Screen Code

Any screen that you want to generate from a template will most likely require some sort of custom code, and the Add/Edit screen is no exception. This screen requires code in its loaded method to create a new instance of your local screen property and to set the value of the local screen property to the results of the underlying screen query.

The final part of Listing 20-22 creates the screen’s .NET code by calling the AddScreenCodeBehind method. A developer can use your screen template from either a VB or C# application. The host.ScreenCodeBehindLanguage property returns the language of the target application, and you can use this information to build your screen's source code in the correct language. The code retrieves a language-specific template from a dictionary called _codeTemplates. It then uses .NET’s String.Format method to substitute the required values into the template (Listing 20-23). The purpose of this code is to recreate the code from the Add/Edit screen sample from Chapter 9.

Another way to generate code is to use .NET’s Code Document Object Model

(CodeDOM). CodeDOM is specially designed for this purpose, and you can read more about it on the following Microsoft web page: http://msdn.microsoft.com/enus/library/650ax5cx.aspx. However, this example uses string substitution because it’s simple to understand and saves you the small task of having to learn a new API.

Creating More Complex Screen Templates

Your Add/Edit screen template is now complete, and you can now build and deploy your extension. The template-generation host allows you to do much more than this chapter shows; for example, you can add query parameters, related collections, and entity properties to your screen. You can use Visual Studio’s IntelliSense to work out the purpose of the host generation methods, so designing more complex screens shouldn’t be difficult.

If you want to create more complex screen templates, my best advice is to create your screen as normal in your LightSwitch application. You can then examine the LSML markup for your screen to help figure out the objects that you need to build in code. This technique comes in handy when you want to work out the correct ViewIDs to use (especially when you use custom controls) and when you want to build ChainExpressions that enable you to access the selected item in a collection. I’ll demonstrate this technique in the next section, where I will show you how to build an HTML screen template extension.

Creating an HTML Screen Template Extension

In this section, I’ll show you how to build an HTML screen template extension. The steps to build one are almost identical to those that you use to build a desktop screen template extension. I’ll show you how to create a template to build a screen that displays a single record. The output screen will include a button that the user can click to delete the record. Before the screen carries out the deletion, it will prompt the user to reconfirm the action with a popup dialog. This screen is identical to the one I showed you in Chapter 8, and you can refer to that chapter to find out more.

Just like the desktop client example, the key to screen template design is to build a screen with the designer in a LightSwitch application. You can use this process to itemize the tasks that you need to carry out. Figure 20-15 illustrates a list of steps that are required to build this screen.

9781484207673_Fig20-15.jpg

Figure 20-15. HTML client screen template steps

To build a template for this screen, add a new screen template item by selecting the right-click menu item on the LSPKG project. Name your screen template ViewDeleteScreenTemplate.

Once you add your template, you must designate it as an HTML client screen template rather than a desktop client screen template. To do this, open the code file for your template and locate the factory class definition. Now, decorate the class with the TargetPlatform attribute value and set the value to MobileWeb, as shown in Listing 20-24 image. For the code to recognize this attribute, use the Imports/Using statement to add a reference to the Microsoft.LightSwitch.DesignTime namespace at the top of your code file.

The next step is to set the properties of your screen template, such as the template name and display name. For this example, set the RootDataSource property value to ScalarEntity. This will configure the screen data drop-down to show singleton queries when a developer chooses your screen template.

To define the code that generates the screen, add the code from Listing 20-25 to the Generate method. To help you follow this code, you can use the comments to associate the blocks of code with the steps that are shown in Figure 20-15.

This code shares many similarities with the code that I showed you in the desktop client example. Here’s an explanation of the more notable parts.

The initial part of this code adds a tab to the screen. To accomplish this, the code calls the AddContentItem method to add a new group to the main tab control content item. The AddContentItem method requires you to supply a parent content item. You can use the host.ScreenTabPagesContentItem member to refer to the tab control item on the screen image.

To add data items for the property, add a rows layout and data bind it to the data source of the screen. You can then generate child data items by calling the ExpandContentItem method image.

You can add a popup with code that looks much the same as the code you use to add a tab control. To add a popup, call the AddContentItem method to add a new group. When you call this method, use the host.ScreenDialogPagesContentItem member to define the screen’s popup folder as the parent content item image.

The next section of code that warrants further explanation is the part that adds a button to the popup control image. To achieve this, the code adds a screen method called DoDelete. The next block of code defines a content item of type Command (this is a content item object that represents a button). The remaining code then associates this content item with the DoDelete method. Note that for brevity I omitted the code that defines the popup’s Cancel button. You can easily add this button by applying the same coding technique.

The next section of code creates the button that opens the popup image. The syntax looks very similar to the code that adds the Delete button. A significant difference is that the code that defines the popup button sets the icon of the button to an image of a trash can.

The code you use to assign an icon isn’t obvious. As I mentioned earlier, the trick to working out what code to use is to examine the LSML on an existing screen. To illustrate this technique, open a LightSwitch project and find a screen that includes a button with a trash can icon. From Solution Explorer, open your screen in the XML editor. To do this, right-click your screen in Solution Explorer and select the “open with” menu item. When the Open With dialog opens, choose the “XML (Text) Editor” item. This opens an XML view of your data, as shown in Figure 20-16.

9781484207673_Fig20-16.jpg

Figure 20-16. LSML view of a screen

From the XML data, you can identify the line that defines the trash icon. This view shows that the LSML defines a trash icon with a ConstantExpression that is wrapped inside ChainExpression, ScreenExpressionTree, and ControlPoperertySource elements. So, to assign the trash can image to a button, you can use the .NET API methods to recreate this XML structure in code. As Listing 20-25 shows, you can declare new ConstantExpression and ChainExpression objects in code. You can then call the host.SetControlPropertyExpressionTree method to assign a control property to the content item.

The final section of code defines the JavaScript code for the screen image. The code uses a string builder object to build the code and then calls the host.AddScreenCodeBehind method to append the JavaScript to the code file of your screen.

Creating a Data-Source Extension

In the final part of this chapter, I’ll show you how to create a data-source extension. Data-source extensions allow developers to consume data sources that are not natively supported by LightSwitch. Although there are several other ways to connect to external data, including RIA Services and OData, the benefit of a data-source extension is that you can more easily package and share the code that consumes a data source. In this section, you’ll learn how to create a data-source extension that connects to the Windows event log on the server. The purpose of this example is twofold. The first is that it demonstrates how to connect to a slightly unusual data source, while the second reason is slightly more practical. You can use this data source to display your server’s event log from within your application so that, once you deploy your application, developers or support staff can view the errors that your application generates without needing access rights to log on to the server. Here’s an overview of the steps to create the Windows event log data-source extension:

  1. Create a new data-source extension and name it WindowsEventLog. To do this, right-click your LSPKG project, select “New Item,” and choose the “Data Source” option.
  2. Add entity classes to represent event sources and event log entries.
  3. Add the RIA Services code that retrieves the event log data.

Creating an Entity Class

Just as in the RIA services code from Chapter 14, you need to define entity classes to enable LightSwitch to consume your data. To carry out this example, you’ll need to create a pair of entity classes: a class that represents an event log entry and a class that represents an event log source. An event log source represents a group of event log entries. (The Application, System, and Security logs in the Windows event log are examples of sources.) To add these classes, create a new class file in your Server project, name it EventLogEntityClasses, and add the code that’s shown in Listing 20-26.

In the code that’s shown in Listing 20-24, notice how the primary-key property is decorated with the key attribute, and how it’s also decorated with the Editable attribute, with the value set to false. LightSwitch uses these attributes to prevent users from editing the ID property and to render it on screens by using read-only controls.

You can specify the Required attribute to define a mandatory property. You can also configure the StringLength attribute to specify the maximum length of a property. Both these attributes enable LightSwitch to apply its built-in validation, and they prevent users from saving data that violates the rules that you specify.

A highlight of this code is that it defines a relationship between the LogSource and LogEntry entities. A single LogSource record can be associated with many LogEntry entries, and the Association attribute defines this relationship between the two entities.

Creating the Data Service Class

The next step is to write the code in your domain service class. Just like the RIA service example, this class contains the logic that adds, updates, retrieves, and deletes the data from your underlying data source. The data-source template creates a domain service class called WindowsEventLog. To implement your domain service class, add the code that’s shown in Listing 20-25.

This code relies on the methods in the System.Diagnostics namespace to retrieve the event log messages. The code decorates the GetEventEntries method with the Query(IsDefault=true) attribute image . This indicates that LightSwitch should use it as the default method for returning a collection. This code includes logic that limits that number of entries to return to 200, and it also includes an error trap that allows the code to skip over event sources that it can’t access because of insufficient permissions. In practice, you can modify this code so that it better handles these conditions.

Because the Windows event log doesn’t allow you to update or delete individual entries, notice that the code doesn’t implement the UpdateLogEntry and DeleteLogEntry methods.

Using the Data-Source Extension

You’ve now completed all of the necessary code to build and use a data-source extension. Once you install your extension, you can use it by going to Solution Explorer in your LightSwitch project, selecting the right-click “Add Data Source” option, and choosing “WCF RIA Service.” In the next dialog that appears, you can find an entry for the Windows event log service in the “Available WCF RIA Service classes” listbox, as shown in Figure 20-17.

9781484207673_Fig20-17.jpg

Figure 20-17. Using the data-source extension

Select the Windows event log service and carry out the remaining steps in the Attach Data Source wizard. Once you complete the steps in the wizard, you can consume the Windows event log in your application, just as you would for any other data source.

Summary

In this chapter, you learned how to create business type, shell, theme, and data-source extensions. Business types are special data types that are based on primitive, basic LightSwitch types. When you add a property to a table, you can specify a business type rather than a basic data type like string or integer. Business types provide two distinct benefits. First, they provide bespoke validation, and second, you can associate screen controls with a business type.

When you create a new business type, the template creates an LSML file in your project for your business type. You use this file to define the metadata for your business type. The definitions you can specify include the underlying data type, any custom validation that you want to apply, and the attributes that you want to expose through the table designer.

To associate your business type with validation code, you specify the name of a validation factory class in your business type’s LSML file. When LightSwitch needs to validate the value of a business type, the factory class returns an instance of a custom validation class that contains your validation logic. The validation code for a business type belongs in the Common project. This is because in the case of a desktop application, the code in the Common project can run on both the server and the client, and this enables LightSwitch to execute the validation in both places.

To give an example, LightSwitch’s phone number business type includes an attribute that the developer can use to set a list of valid phone number formats for an instance of the business type. The developer can edit this list by opening a popup dialog from the table designer’s property sheet. In this chapter, I showed you how to add your own custom popup dialogs to Visual Studio’s property sheet.

This technique relies on a WPF control that defines your popup editor control. To link this control with an attribute, you specify an attribute in your business type’s LSML file and define a “UI Editor” setting that links the attribute with a factory class. The factory class returns an instance of an editor class that produces the UI that Visual Studio displays in the property sheet. This UI can contain controls that the developer can use to edit your attribute, such as textboxes or checkboxes. But in this case, the UI simply defines a hyperlink that opens the popup dialog. Once the developer edits the value, the editor class updates the underlying attribute through an IPropertyEntry object that the factory class provides.

Custom shells enable developers to modify the structure and overall layout of an application. A shell consists of an XAML file that defines the layout of the UI, and also contains controls to manage commands, navigation, and screen interaction. View models allow the UI to consume the application data that LightSwitch exposes. With these view models, you can access navigation items, commands, active screens, and validation details.

LightSwitch includes a Component View Model Service that provides easy access to the view models in your XAML code. You can simply add a line of code in your Silverlight control that references the service and supply a view model ID. This sets the data context of your control to the view model, and you can then data bind the properties of your control to the properties that the view model exposes. A custom shell requires you to write the .NET code that executes commands, manages screens, and performs navigation.

In this chapter, I showed you how to create a ScreenWrapper object that determines if a screen contains data changes or validation errors. I also showed you how to build custom navigation by implementing a combo box control that enables users to open screens. This control data binds to a collection of INavigationScreen objects through the navigation view model. When a user selects a screen, it triggers code that prompts the LightSwitch runtime to open the selected screen. Although the runtime opens the screen, it doesn’t display any content to the user—this is something that you need to implement yourself. To accomplish this, you can employ a ServiceProxy object to notify you whenever the runtime opens a screen. When this event occurs, you can execute code that displays the screen by adding the content to a tab control.

Custom themes enable developers to customize the font, color, and styles that a shell applies. A custom theme defines the styles within an XAML file in your extension project. When you add a new theme, the template creates a working theme that contains well-commented sections that describe each style setting. For example, if you want to change the command-bar style, you can use the comments to find the command-bar section and amend the entries within that section to modify the colors, font names, and font styles.

If you carry out the same repetitive tasks in the screen designer, you can automate the process by creating a custom screen template extension. You define a screen template with .NET code that belongs in the Design project of your extension solution. The code file for your template includes .NET properties you can use to specify the settings of your template. These settings include the name, description, and root data-source type of your template. These settings appear in the Add New Screen dialog when a developer adds a new screen in a LightSwitch application. The root data-source type property is important because it defines the data that fills the “Screen Data” drop-down in the Add New Screen dialog.

The .NET method that builds a screen is called Generate. This is the method where you add code to generate your output screen. Inside this method, you can access a screen generator host object to carry out screen-design tasks in code. With this object, you can carry out the same screen-design tasks in code as you would carry out with the screen designer. For example, you can use the host to create local screen properties, add content items, and change the control that renders a data item. The host generator object includes a method you can use to add code to your screen. When you create a desktop screen template, you should write code to build VB and C# versions of your screen code. In the case of HTML client screen templates, you can use the host generator object to add JavaScript code to your output screen.

In the final part of this chapter, I showed you how to create a data-source extension. You can build data-source extensions to enable developers to connect to data sources that are not natively supported by LightSwitch. The code in a data-source extension relies on a domain service class, so the code that you use looks very similar to the RIA service code from Chapter 14. In this chapter, I showed you how to create a data-source extension that connects to the Windows event log. To create a data-source extension, you first need to add entity classes that describe the data that your extension returns. You can optionally create associations to define relations between the entity classes in your data source. You can add methods to your domain service class to retrieve, add, update, and delete the data from the underlying data source. Just as if you were writing an RIA service, you must define a default method that returns a collection of data. You achieve this by adding a method that returns data and decorating it with the Query(IsDefault=true) attribute.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset