Thursday, November 20, 2008

F# and Silverlight 2.0

One of the appeal of learning WPF is that now I can leverage what I learned in WPF and apply them to building web applications via Silverlight 2.0. This blog talks about setting up a very simple Silverlight 2.0 web application with F#.

Since my laptop is running Windows XP, I needed to build an environment with which I can host my Silverlight application. I decided to build a Virtual PC environment running Windows Server 2008. I'm using F# version 1.9.6.2 to build this sample Silverlight application.

Below is the simple Silverlight application with only a single clickable button:


#light
namespace SilverLightFSharp
open System
open System.Windows
open System.Windows.Controls

type MyPage = class
inherit UserControl

new () as this = {} then
// Add button and do something with it...
let btn = new Button(HorizontalAlignment = HorizontalAlignment.Center,
VerticalAlignment = VerticalAlignment.Center)
btn.Content <- "Click me"
btn.Click.Add(fun _ ->
btn.Content <- "The button has been clicked!")
this.Content <- btn

end

type MyApp = class
inherit Application

new () as this = {} then
this.Startup.Add(fun _ -> this.RootVisual <- new MyPage())
//base.Exit.Add( fun _ -> ()) //this.Application_Exit)
//this.InitializeComponent()

end

I also needed some scaffolding in order to get this Silverlight application to run. One of the scaffolding pieces I need is the AppManifest.xaml file. Below is the content of that AppManifest.xml file with the EntryPointAssembly and EntryPointType attributes set to my F# code.


<Deployment xmlns="http://schemas.microsoft.com/client/2007/deployment"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
EntryPointAssembly="SilverLightFSharp"
EntryPointType="SilverLightFSharp.MyApp" RuntimeVersion="2.0.31005.0">
<Deployment.Parts>
<AssemblyPart x:Name="SilverLightFSharp" Source="SilverLightFSharp.dll" />
<AssemblyPart x:Name="FSharp.Core" Source="FSharp.Core.dll" />
<AssemblyPart x:Name="System.Windows.Controls" Source="System.Windows.Controls.dll" />
</Deployment.Parts>
</Deployment>

I'll also need a test html page to host my silverlight application. I created a TestPage.html to host my Silverlight application. Please note that for the Silverlight control host has the source referencing the file simplebutton.xap. Not to worry, simplebutton.xap is merely a zip file that contains the following files:

  • AppManifest.xaml
  • FSharp.Core.dll
  • System.Windows.Control.dll
  • SilverlightFsharp.dll

Here's the TestPage.html code:


<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<!-- saved from url=(0014)about:internet -->
<head>
<title>SilverlightFSharp</title>

<style type="text/css">
html, body {
height: 100%;
overflow: auto;
}
body {
padding: 0;
margin: 0;
}
#silverlightControlHost {
height: 100%;
}
</style>

<script type="text/javascript">
function onSilverlightError(sender, args) {

var appSource = "";
if (sender != null && sender != 0) {
appSource = sender.getHost().Source;
}
var errorType = args.ErrorType;
var iErrorCode = args.ErrorCode;

var errMsg = "Unhandled Error in Silverlight 2 Application " + appSource + "\n" ;

errMsg += "Code: "+ iErrorCode + " \n";
errMsg += "Category: " + errorType + " \n";
errMsg += "Message: " + args.ErrorMessage + " \n";

if (errorType == "ParserError")
{
errMsg += "File: " + args.xamlFile + " \n";
errMsg += "Line: " + args.lineNumber + " \n";
errMsg += "Position: " + args.charPosition + " \n";
}
else if (errorType == "RuntimeError")
{
if (args.lineNumber != 0)
{
errMsg += "Line: " + args.lineNumber + " \n";
errMsg += "Position: " + args.charPosition + " \n";
}
errMsg += "MethodName: " + args.methodName + " \n";
}

throw new Error(errMsg);
}
</script>
</head>

<body>
<!-- Runtime errors from Silverlight will be displayed here.
This will contain debugging information and should be removed or hidden when debugging is completed -->
<div id='errorLocation' style="font-size: small;color: Gray;"></div>

<div id="silverlightControlHost">
<object data="data:application/x-silverlight," type="application/x-silverlight-2" width="100%" height="100%">
<param name="source" value="simplebutton.xap"/>
<param name="onerror" value="onSilverlightError" />
<param name="background" value="white" />
<param name="minRuntimeVersion" value="2.0.31005.0" />
<param name="autoUpgrade" value="true" />
<a href="http://go.microsoft.com/fwlink/?LinkID=124807" style="text-decoration: none;">
<img src="http://go.microsoft.com/fwlink/?LinkId=108181" alt="Get Microsoft Silverlight" style="border-style: none"/>
</a>
</object>
<iframe style='visibility:hidden;height:0;width:0;border:0px'></iframe>
</div>
</body>
</html>

After I copy simplebutton.xap and TestPage.html to my IIS webserver, I am rewarded with the following results:

5 comments:

Art said...

Thanks John.
E = F# + Ag c

DannyAsher said...

Great news that F# works with Silverlight! I was unable to reproduce your results though. Could you post your solution? Did you have to compile FSharp.Core.dll against agcore.dll? And perhaps explain why Sever 2008 is required (rather than XP - which serves my Silverlight pages well.)

many thanks,
Danny

John Liao said...

You don't need to use Server 2008. I just needed a server to host IIS and my Silverlight application. I don't have the full version of Visual Studio 2008, I have Visual Studio Shell w/F# compiler installed on my XP. If you're trying to execute this within your Visual Studio environment, I'm not sure I know how it will behave.

I deployed this to IIS with the Silverlight SDK installed. I did not have to compile FSharp.Core.dll against agcore.dll. My Visual Studio generated the following compiler command.

fsc.exe -o obj\Debug\SilverlightFsharp.dll -g --noframework --define TRACE --define DEBUG -r "C:\Program Files\FSharp-1.9.6.2\\bin\FSharp.Core.dll" -r "C:\Program Files\Reference Assemblies\Microsoft\Framework\v3.5\System.Core.dll" -r C:\Windows\Microsoft.NET\Framework\v2.0.50727\System.dll -r "..\..\..\..\..\..\..\Program Files\Microsoft SDKs\Silverlight\v2.0\Reference Assemblies\System.Windows.Browser.dll" -r "..\..\..\..\..\..\..\Program Files\Microsoft SDKs\Silverlight\v2.0\Libraries\Client\System.Windows.Controls.Data.dll" -r "..\..\..\..\..\..\..\Program Files\Microsoft SDKs\Silverlight\v2.0\Libraries\Client\System.Windows.Controls.dll" -r "..\..\..\..\..\..\..\Program Files\Microsoft SDKs\Silverlight\v2.0\Reference Assemblies\System.Windows.dll" -r "..\..\..\..\..\..\..\Program Files\Microsoft SDKs\Silverlight\v2.0\Reference Assemblies\System.Xml.dll" --target library --warn 3 --warnaserror 76 --vserrors --fullpaths simplebutton.fs

You can ignore some of the extra DLLs that I've added. The DLL list in the appmanifest.xaml should be sufficient for it to work.

Anonymous said...

A late comment, I have seen VS 2008 act quite a bit different than the VS Shell.

I have included my URL, but I haven't done much with it lately. John you motivate me to get sharp with F# again.

digital signature software said...

Thanks! This article was very helpful and saved me many hours of digging and experimentation.