Visual Studio を使わずに F# で Silverlight 4 のサンプル作成 - fsc.exe を用いた手動作成

Visual F# 等の助けを借りず、Silverlight 4 の簡単なサンプルを fsc.exe を使って手動作成してみました。

以前 id:fits:20080310 に Silverlight 2 のサンプルを IronPython で作った方法と似てますが、今回は AppManifest.xaml も自前で用意するので Chiron.exe も必要ありません。(ただし、zip 圧縮の手段は必要)

とりあえず、以下と Silverlight 4 の SDK をインストールしておきます。(今回の用途では Silverlight のランタイムだけあればよくて SDK は要らないかも)

サンプルソースは http://github.com/fits/try_samples/tree/master/blog/20101017/

アプリケーション作成

まず、メインアプリケーションの UI を XAML で記述します。

app/MainPage.xaml
<UserControl 
    xmlns="http://schemas.microsoft.com/client/2007"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    x:Class="System.Windows.Controls.UserControl"
    x:Name="Page">

    <StackPanel Background="White">
        <TextBlock Name="label1" Text="サンプルリスト" />
        <ListBox Name="listBox1" Margin="10 10 0 0" HorizontalAlignment="Left" Width="300" Height="200" >
            <ListBox.ItemTemplate>
                <DataTemplate>
                    <StackPanel Orientation="Horizontal">
                        <TextBlock Text="{Binding Path=Id}" />
                        <TextBlock Text="{Binding Path=Title}" Margin="30 0 0 0" />
                    </StackPanel>
                </DataTemplate>
            </ListBox.ItemTemplate>
        </ListBox>
    </StackPanel>
</UserControl>

画面イメージは以下の通り。

次に、上記で作成した XAML(MainPage.xaml)を読み込んで表示するメインアプリケーションを F# で実装します。(Visual StudioSilverlight を作成する場合の App.xaml・App.xaml.cs 等に相当)

処理内容は以下の通り。

  • do バインディング内で Startup イベントのイベントハンドラ(ラムダ式を使用)を追加
  • do バインディング内で this を使用するため "as this" を定義
  • ":?> ListBox" で ListBox 型へダウンキャスト(C# の "as 型" みたいなもの)
  • ListBox の表示データに F# のレコード型を使用(定義は type Data の箇所、インスタンス作成は {Id = ・・・} の箇所)
src/ListSampleApp.fs
namespace ListSample

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

//表示用データ定義(レコード型)
type Data = {
    Id: string
    Title: string
}

//アプリケーションクラスの定義
type ListSampleApp() as this =
    inherit Application()

    //初期化処理
    do
        let xamlUri = new Uri("MainPage.xaml", UriKind.Relative)

        //Startup イベント時の処理を追加
        this.Startup.AddHandler(fun _ _ -> 
            let control = new UserControl()
            //XAML 読み込み
            Application.LoadComponent(control, xamlUri)
            //コントロールの表示
            this.RootVisual <- control

            //ListBox 取得
            let listBox = control.FindName("listBox1") :?> ListBox
            //ListBox への表示用データ設定(Data のインスタンスを作成)
            listBox.ItemsSource <- [
                {Id = "A001"; Title = "XAMLファイルを出力する方法"}
                {Id = "A002"; Title = "WPF レイアウト"}
                {Id = "B001"; Title = "F# ラムダ式の記法"}
            ]
        )

なお、ListBox の取得時にダウンキャストを使ってますが、パターンマッチを使った方が望ましいかもしれません。

ビルド

fsc.exe で src\ListSampleApp.fs をビルドして app ディレクトリに ListSample.dll ファイルを作成します。
fsc.exe には以下のようなオプションを指定する必要があります。

  • "--noframework" で通常の .NET Framework が参照されるのを防止
  • F# の Silverlight 用アセンブリ FSharp.Core.dll を参照指定
  • 実装内容に応じて Silverlight 用の各種アセンブリを参照指定

なお。fsc.exe は .NET 2.0 用のものを使う点に注意。(例 C:\FSharp-2.0.0.0\bin\fsc.exe を使う)

ビルドの内容をバッチファイル化すると以下のような内容になります。(アセンブリの位置は環境に応じて変更が必要)

ビルド用バッチファイル例 build.bat
set FSHARP_SL_LIB=C:\FSharp-2.0.0.0\Silverlight\2.0\bin
set SL_LIB=C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\Silverlight\v4.0

fsc.exe -a --noframework --out:app\ListSample.dll -r:"%FSHARP_SL_LIB%\FSharp.Core.dll" -r:"%SL_LIB%\System.dll" -r:"%SL_LIB%\System.Windows.dll" src\ListSampleApp.fs

copy %FSHARP_SL_LIB%\FSharp.Core.dll app\

なお、FSharp.Core.dll は後で XAP ファイルに含める事になるので、app ディレクトリにコピーしておきます。

C:\FSharp-2.0.0.0\bin を環境変数 PATH に設定しておき、build.bat を実行します。

build.bat 実行例
> build.bat

AppManifest.xaml 作成

AppManifest.xaml を作成します。(Visual Studio や Chiron.exe とかだと自動で作ってくれる)
ListSample.dll・FSharp.Core.dll 用の AssemblyPart の定義、EntryPointAssembly にアプリケーションのアセンブリ・EntryPointType にアプリケーションクラスをそれぞれ設定します。

app/AppManifest.xaml
<Deployment 
    xmlns="http://schemas.microsoft.com/client/2007/deployment" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    EntryPointAssembly="ListSample" 
    EntryPointType="ListSample.ListSampleApp" 
    RuntimeVersion="4.0.50826.0">

    <Deployment.Parts>
        <AssemblyPart x:Name="ListSample" Source="ListSample.dll" />
        <AssemblyPart Source="FSharp.Core.dll" />
    </Deployment.Parts>
</Deployment>

XAP ファイル作成

これまでに作成したファイル(app ディレクトリ内の配置されているはず)を zip 圧縮して XAP ファイル(ListSample.xap)を作成します。(app ディレクトリを含めない点に注意)

今回は署名とかしないので、普通に zip 圧縮して必要に応じて拡張子を変更すれば OK です。

app ディレクトリ内の構成例
  • app
    • AppManifest.xaml
    • FSharp.Core.dll
    • ListSample.dll
    • MainPage.xaml
ListSample.xap ファイル内の構成例
  • AppManifest.xaml
  • FSharp.Core.dll
  • ListSample.dll
  • MainPage.xaml

ちなみに、IronRuby 1.1 等に含まれている Chiron.exe(silverlight\bin\Chiron.exe)を使って XAP ファイルを作成するには以下のように /x オプションを使います。

Chiron.exe での作成例
> Chiron.exe /d:app /x:ListSample.xap

動作確認

以下のような Silverlight 実行用の HTML を用意して、Web ブラウザで実行します。

index.html
<!DOCTYPE html>
<html>
<head>
<title>ListSample</title>
<link rel="stylesheet" href="default.css" type="text/css" />
<script type="text/javascript" src="Silverlight.js"></script>
</head>
<body>
    <div id="silverlightControlHost">
        <object data="data:application/x-silverlight-2," type="application/x-silverlight-2" width="100%" height="100%">
            <param name="source" value="ListSample.xap"/>
            <param name="background" value="white" />
            <param name="minRuntimeVersion" value="4.0.50826.0" />
        </object>
    </div>
</body>
</html>