Поделиться через


Tutorial: Creating a Silverlight Project in F# Using the Online Templates

Applies to: Functional Programming

Authors: Tomas Petricek and Jon Skeet

Referenced Image

Get this book in Print, PDF, ePub and Kindle at manning.com. Use code “MSDN37b” to save 37%.

Summary: This article looks at how to use online templates to create a simple F# Silverlight application and how to add a basic user interface.

This topic contains the following sections.

  • Creating Silverlight Applications
  • Using Online Templates
  • Creating a Bar Code Scanner
  • Summary
  • Additional Resources
  • See Also

This article is associated with Real World Functional Programming: With Examples in F# and C# by Tomas Petricek with Jon Skeet from Manning Publications (ISBN 9781933988924, copyright Manning Publications 2009, all rights reserved). No part of these chapters may be reproduced, stored in a retrieval system, or transmitted in any form or by any means—electronic, electrostatic, mechanical, photocopying, recording, or otherwise—without the prior written permission of the publisher, except in the case of brief quotations embodied in critical articles or reviews.

Creating Silverlight Applications

When creating F# projects in Visual Studio, it is possible to start with one of the pre-installed templates for creating basic types of projects. The templates include a basic console application, library, and Silverlight library. F# is not limited to these types of projects and can be used in a wide range of other scenarios including both server-side and client-side web applications. This tutorial shows how to create a standalone client-side component in F# using Silverlight 4.

The easiest way to create a Silverlight project in F# is to develop a Silverlight library (using the F# Silverlight Library template) and reference it from a C# application. Microsoft® Visual Studio 2010 doesn't come with a template for a standalone F# Silverlight application, but many additional templates can be found under Online Templates. This tutorial starts with one of the online templates and uses it to build a simple control for entering numeric bar codes.

Using Online Templates

To create a new project using an online template, go to the New Project dialog as usual. In the dialog, select Online Templates from the pane on the left side. Online templates are submitted by the community and include a wide range of different templates. A search for "FSharp," "fsharp," "F#," "F# Silverlight," or "F# application" reveals a large number of useful starting points for different types of projects.

Creating an F# Client-Side Application

This tutorial uses the F# Client-Side Application (Silverlight)template. Figure 1 shows the template selected in the New Project dialog.

Figure 1. A "New Project" dialog showing online F# templates

Referenced Image

After specifying the project name with a location and clicking OK, Microsoft® Visual Studio asks the user to agree with the template license terms. The template used in this tutorial (as well as most of the other F# templates) is available under the permissive MS-PL license.

The F# Client-Side Application template creates a single project that contains a customized F# Project that builds a Silverlight .xap file, which can be embedded in web pages. The next section explores the structure of the template. Other F# Silverlight templates also include an ASP.NET web application, so they can be used for creating both client-side and server-side parts of the application.

Exploring the Template Structure

After creating a new F# Client-Side Application project, the Solution Explorer window should contain three files. The AppManifest.xml file can be used to configure the application. For example, it can be changed to create a desktop application (using the out-of-browser Silverlight execution mode). For more information about how to do this, see the references at the end of this article.

The remaining two files contain the application's F# source code. The Page.fs file implements the main control and specifies the user interface that appears when the application starts. The App.fs file contains the entry point object that starts the application. The order of the file matters because App.fs uses controls defined in Page.fs. The following listing contains the user interface code in Page.fs:

namespace BarcodeScanner

open System.Windows.Controls
open System.Windows.Media

type AppControl() =
    inherit UserControl(Width = 800.0, Height = 600.0)
    // Initialize controls to be added to the page
    let clr = Color.FromArgb(255uy, 120uy, 180uy, 60uy)
    let fill = SolidColorBrush(clr)
    let canv = new Canvas(Background = fill)
    let block = new TextBlock(Text="Hello world!", FontSize = 30.0)

    do  // Move text block to the center, add it to canvas 
        block.SetValue(Canvas.LeftProperty, 280.0)
        block.SetValue(Canvas.TopProperty, 200.0)
        canv.Children.Add(block)
        // Display the canvas in the user control
        base.Content <- canv

The project consists of multiple files, so the file contains a namespace declaration. The namespace name is automatically generated from the project name. The file declares a single type named AppControl that inherits from the UserControl class. This class represents a visual element. The snippet sets the width and height of the control when calling the base constructor.

The rest of the snippet constructs a simple user interface. It creates a TextBlock with contents "Hello world!" and places it on a newly created Canvas. Moving the element to the center of the canvas is done using the Left and Topdependency properties of the Canvas type. These are not exposed as standard .NET properties. Instead, they are updated using the SetValue method. Finally, the last line sets the Content property of the UserControl to the created Canvas.

The second file of the template (named App.fs) represents the entire Silverlight application. When the application starts, it loads the AppControl and uses it as the main visual element of the application:

namespace BarcodeScanner
open System.Windows

type App() as this =
    inherit Application()
    // Create the main application control
    let main = new AppControl()
    do  // Display the control when application starts
        this.Startup.Add(fun _ -> 
            this.RootVisual <- main)

The type inherits from the Application class. It is used as the entry-point of the Silverlight application. When the application starts, it runs the constructor that creates a new AppControl and registers an event handler for the Startup event. The snippet needs to use the as this syntax when defining the type so that it can access the currentinstance in the constructor. When the Startup event occurs, the handler sets the RootVisual property, which represents the main visual element of the application and must be set only once during application startup.

Creating a Bar Code Scanner

The rest of this tutorial builds an example based on the template. It creates a simple user interface for entering numeric bar codes. Although this is a basic application, it demonstrates working with Silverlight from F#. This tutorial constructs the user interface without using XAML. This approach is great for simple applications with programmatically generated and updated user interfaces, such as games.

The end of the document provides links to more complete tutorials as well as tutorials that demonstrate how to use XAML in F#.

Simplifying Dependency Properties

We will define two extensions for setting dependency properties that specify the location of a control inside a Canvas. Silverlight defines them as dependency properties because they can be set only when a control is located inside a canvas. This sample places all of the controls inside a canvas, so it makes sense to create extension properties that provide a nice syntax for setting the location of any Silverlight Control:

[<AutoOpen>]
module Extensions =
    type System.Windows.Controls.Control with
        member x.CanvasLeft 
            with set(v:float) = x.SetValue(Canvas.LeftProperty, v)
            and get() = x.GetValue(Canvas.LeftProperty) :?> float
        member x.CanvasTop
            with set(v:float) = x.SetValue(Canvas.TopProperty, v)
            and get() = x.GetValue(Canvas.TopProperty) :?> float

All extensions must be placed inside a module, so the snippet declares an Extensions module. The module is marked using the AutoOpen attribute, so the extensions will automatically be available. A type extension can be defined using the type <full-name> with syntax. This snippet defines two properties that can be used to set and get values of dependency properties Canvas.Left and Canvas.Top. These are simple helpers, but they make the construction of textboxes and buttons nicer.

Creating a Textbox and Buttons

To construct the user interface shown in Figure 2, the application needs to create a textbox and several buttons. There is just one textbox, so it is created directly:

let input = new TextBox(Text="", FontSize = 16.0, Width = 130.0)
input.CanvasLeft <- 50.0
input.CanvasTop <- 50.0
canv.Children.Add(input)

This snippet creates a new textbox and sets its properties. The new extension properties are used to set the textbox location. The values of intrinsic properties are specified in the constructor call but the extension properties have to be set using the <- operator.

As Figure 2 shows, the sample application has quite a few buttons. To avoid creating all of them by hand, the code uses the following helper function:

let createButton (value:string) handler (x, y) = 
    let button = new Button( Content = value, 
                             Width = 40.0, Height = 40.0)
    button.CanvasLeft <- float x * 45.0 + 50.0
    button.CanvasTop <- float y * 45.0 + 100.0
    button.Click.Add(fun _ -> handler())
    canv.Children.Add(button)

The function takes the text to appear on the button, a function that specifies a handler and the location of the button. It creates a button of a fixed size and moves it to the given coordinates (the coordinates are just indices in the grid, so the function first calculates the actual position). The event handler for Click ignores the argument, so the type of the handler function is just unit -> unit.

Given this function, the user interface can be completed quite easily. Because the createButton function takes coordinates as the last argument, the construction can be done elegantly using partial function application.

Completing the Control Pad

The control pad consists of three types of buttons. The first three rows contain buttons for entering the numbers 1 to 9. The last row consists of a button that resets the entered text, a button that adds 0, and a button that submits the entered bar code (which is not implemented in this sample).

The following snippet defines the function for creating each type of button. It uses the createButton function and specifies just the button label and the handler:

let resetButton = createButton "X" (fun () -> input.Text <- "")
let submitButton = createButton "OK" (fun () -> input.Text <- "TODO!")
let numButton value = createButton (string value) (fun () ->
    match Int32.TryParse(input.Text + (string value)) with 
    | true, n -> input.Text <- string n
    | _ -> input.Text <- string value)

In all three cases, the last argument of the createButton function is omitted. This means that the type of resetButton and submitButton functions is (int * int) -> unit. When using them later, they will be given the coordinates of the button. The numButton function has an additional parameter that specifies the number displayed on the button.

The only interesting handler is the one specified in the numButton function. It checks whether appending a number to the current text gives a valid integer. If yes, then the handler displays the parsed number (which removes unnecessary leading zeros). If no, then the handler only displays the number that was just entered. This resets the input after submitting the previous bar code.

The snippet that generates buttons (in the constructor of AppControl) just needs to generate a grid with buttons for entering numbers 1 through 9 and then add an additional row with reset, zero, and submit:

for x in 0 .. 2 do
    for y in 0 .. 2 do
        numButton (y * 3 + x + 1) (x, y)
resetButton (0, 3)
numButton 0 (1, 3)
submitButton (2, 3)

The two nested for loops first generate a regular grid with buttons from 1 to 9. The last row is not regular, so it is created explicitly by calling the three helper functions with the coordinates as the last argument. The resulting Silverlight user interface is shown in Figure 2.

Figure 2. Entering bar codes (Silverlight running in a web browser)

Referenced Image

When the user clicks on the numeric buttons, the numbers are added to the textbox input. The X button erases the input. The OK button would submit the input for processing. In this tutorial, it just displays a message. Although this tutorial was quite simple, it demonstrated a complete Silverlight application. Additional Resources provides links to more examples of implementing user interactions and communicating with the server-side backend.

Summary

This tutorial discussed how to create a Silverlight application in F# using an online template. F# can be used to develop a wide range of project types, and skeletons of many of them can be found as Online Templates available from Microsoft® Visual Studio 2010. In this tutorial, we started with a template that creates a standalone Silverlight application, but there are other templates that include the server-side part of the application in ASP.NET.

The user interface in this article was generated programmatically from F#. This approach is suitable when creating a dynamic user interface (such as a game). On the other hand, when creating a complex design, one would typically use XAML files. More information about this approach is available below.

Additional Resources

This tutorial demonstrates how to create a simple Silverlight project using an online template. For more information about implementing reactive user interfaces, handling events, and making calls to the server-side part of an application, see the following tutorials:

This article didn't show how to use XAML in an F# Silverlight project. For more information about creating and embedding XAML as well as accessing XAML controls, see the following how-to pages:

To download the code snippets shown in this article, go to https://code.msdn.microsoft.com/Chapter-3-Reactive-Client-8a458f7d

See Also

This article is based on Real World Functional Programming: With Examples in F# and C#. Book chapters related to the content of this article are:

  • Book Chapter 9: “Turning values into F# object types with members” explains how to use object-oriented features of the F# language. This is an important topic for client-side development because it explains how to mix the object-oriented Silverlight design with the functional programming style.

  • Book Chapter 16: “Developing reactive functional programs” discusses how to write reactive user interfaces using asynchronous workflows and events. It also includes a basic introduction to the Reactive Framework (Rx).

The following MSDN documents are related to the topic of this article:

Previous article: Overview: Reactive Client-Side Programming

Next article: Tutorial: Reactive Programming Using Events