会社の、新技術キャッチアップ プロジェクトで調べたことをお送りしてきた Silverlight 奮戦記ですが、このたび、成果発表会が行われることになりました。
発表時間はごくわずかなので、デモを中心に「こんなことが出来ます」を、Google Web Toolkit、Adobe Integrated Runtime, Silverlight で行うことになったのですが、Silverlight で担当するのが、以前に Windows Application として作成した、「任意の絵をバックグラウンドにして設備管理をする」アプリケーションの、「任意の絵をバックグラウンドにする」の部分となりました!!わーわーわー!!って、喜んでる場合じゃねーよorz
Windows Application の場合、ローカルにあるファイルに、ローカル アプリケーションがアクセスすることになります。何も難しいことはありません。
Silverlight の場合、ローカルにあるファイルにアクセスできないはずなので、一旦 ASP.NET 等でサーバーにアップロードし、Silverlight アプリケーションがダウンロードして使用する、という形になります。
ということは、Silverlight 単体では出来ない、ということです。ASP.NET 他、他の Web Application とのコンビネーションになります。
まずは、開発環境から。β1の頃に書いたけど、RTM しているので、RTM バージョンということで。
Visual Studio 2008 Standard Edition 以上を用意します。それに、Service Pack 1 をあてます。場合によっては、SP1 準備ツールとかいうものを適用しておく必要があるかもしれません。SP1 をあてたら、インテリセンスが英語になってしまう不具合があるので、それを解消するパッチをあてます。
次、Silverlight 本体。「開発環境なんざいらねぇぜ。テキスト エディタとコンパイラで作ってやるぜ!」という強者は、Visual Studio も要りません。Silverlight SDK のみを。普通の方は、Visual Studio 2008 SP1 用 Silverlight Tools を、ダウンロードします。
また、Silverlight2 のドキュメントが、CHM 形式でダウンロードできます。一応、Visual Studio の Document Explorer にインテグレートされますが、[F1] 等で参照しようとすると、高確率で WPF のヘルプが出てきます。Silverlight の CHM ファイルを検索すれば、Silverlight のものしか出てきませんので、検索するときには CHM ファイルのほうが便利だと思います。
MSDN ライブラリには、オンラインのドキュメントも、用意されています。
以上、環境整備終わり。
それではまず、下準備。これ、とっても重要。
上に「Silverlight の場合、ローカルにあるファイルにアクセスできないはず」とか書いたけど、そうなんだっけ?ってことで、「silverlight file upload」で検索すると、CodePlex に Silverlight File Upload というコントロールが見つかった。じゃぁ、こいつはどうやってローカル ファイルにアクセスしているんだろう?ってんで MSDN を調べると、OpenFileDialog クラスが見つかった。これでローカル ファイルにアクセスすることが出来る。
では、画像の表示。Windows Application では、Bitmap クラスを使用する。Silverlight ではどうかというと、そもそも System.Drawing 名前空間がない。では、他には?と探す…のは面倒なので、CHM ファイルで「Bitmap」を検索する。すると、Syste.Windows.Interop 名前空間に、HostingRenderTargetBitmap クラスというのが見つかる。でもこれにはアプリケーション コードでこのメンバを使用すると、MethodAccessException 例外がスローされます。
などと書いてあるので、使えない。次、次。「目次」の「ビジュアル デザイン」を開くと、「グラフィクス」という項目がある。ここの中に「イメージング」があるので、これを開く。すると、イメージを描画するには、Image オブジェクトまたは ImageBrush オブジェクトを使用します。
とある。Image クラスの説明には、BitmapImage クラスへのリンクもある。このふたつのクラスについて、調べる。すると、Image クラスはインターネットの向こうからしかイメージを取得できない。BitmapImage クラスも同じ。・・・困った。せっかく、ローカル ファイルへのパスがわかっても、そこから画像オブジェクトを作成することが出来ない。分離ストレージに放り込んで、そこからストリームで画像を作成してやろうかと思っていたのだけれど、メモリ上で画像を扱えないのではこの方法は使えない。
困った。。。
じゃぁ、あれだ。他のウェブ アプリケーションでサーバーにファイルをアップロードして、そいつを特定の名前で XAP ファイルと同じディレクトリにおいて、Silverlight からはそのファイルを参照すればいい、ってことですね。
決まったので、次、プロジェクトを作成する。
Visual Studio 2008 を起動する。新しいプロジェクトを作成する。とりあえず、ソリューション名を「SilverlightDemo」、プロジェクト名を「BackgroundMap」とする。
すると、Silverlight アプリケーションをホストするものをどうするか、聞いてくる。先にも書いたように、どうにかして、サーバーにファイルをアップロードしなければならない。なので、ウェブ アプリケーションを作成することにする。すると、「BackgroundMap.Web」という名前の ASP.NET アプリケーションが作成される。
ちょい見すると、「BackgroundMapTestPage.aspx」というページが作られている。この中に、Silverlight をホストするコードが書かれている。しかし、このページにはビハインド コードがない。そこで、ホストするためのコードを Default.aspx にコピーして、デバッグのスタート ページも Default.aspx に変更する。
コピーした、Silverlight をホストするコード
<%@ Register Assembly="System.Web.Silverlight"
Namespace="System.Web.UI.SilverlightControls" TagPrefix="asp" %>
(飛ばして)
<asp:ScriptManager ID="ScriptManager1" runat="server">
</asp:ScriptManager>
<div style="height: 100%;">
<asp:Silverlight ID="Xaml1" runat="server" Source="~/ClientBin/BackgroundMap.xap"
MinimumVersion="2.0.31005.0" Width="100%" Height="100%" />
</div>
さて、サーバーにファイルをアップロードして、どこかに保存しておくコードを作る。
FileUpload コントロールと、ポストバックを発生させるための Button コントロール、エラーが発生したときにメッセージを表示する Label コントロールを追加する。
FileUpload コントロールの説明を参照すると、HasFile プロパティというのがある。これで、ファイルを指定せずに「更新」ボタンを押したときの判定が出来る。FileBytes プロパティで、アップロードされたファイルのバイト配列が取得できる。このバイト配列を元にメモリ ストリームを作り、メモリ ストリームから Bitmap オブジェクトを作成、そのオブジェクトの Save メソッドで、ファイルに保存してやればいいだろう。と、大まかに流れを組み立てる。
では、「画像ファイル」として、「画像ではないファイル」を指定されたらどうするか。Bitmap クラスのコンストラクタを見ると、ArgumentException 例外を発生させるらしい。とりあえず、これだけを受けて、検査例外に読み替えてやればいいだろう。
ClientBin へのパスは、どうやって取得する?HttpRequest クラスに、Default.aspx への仮想パス名を格納したプロパティがあるはず。そこから MapPath メソッドで物理パス名に変換、System.IO.Path クラスの GetDirectoryName メソッドでパスに変換して、ClientBin を Combine してやればいいだろう。
ってことで、コード。
アップロードされたファイルを保存する
protected void Page_Load(object sender, EventArgs e) {
Label1.Text = "";
if (IsPostBack == false) {
// 初期表示なのでファイルはないはず
return;
}
if (this.FileUpload1.HasFile == false) {
// ファイルが添付されていないので、以下の処理は必要なし
return;
}
try {
SaveBitmapFile();
} catch (ArgumentException) {
// 指定されたファイルが画像ではないと思われる。
// 例外は握りつぶす。
Label1.Text = "指定されたファイルは、画像ではないようです。画像を指定してください。";
}
}
private void SaveBitmapFile() {
// アップロードされたファイルをメモリ ストリームに取り込む。
using (MemoryStream memStrm = new MemoryStream(this.FileUpload1.FileBytes, false)) {
// メモリ ストリームから、ビットマップに変換する。
using (Bitmap map = new Bitmap(memStrm)) {
// App_Data へ、
string saveFile = System.IO.Path.Combine(
System.IO.Path.GetDirectoryName(
this.MapPath(this.Request.FilePath)), @"ClientBin\MapData.png");
// PNG 形式で保存する。
map.Save(saveFile, System.Drawing.Imaging.ImageFormat.Png);
}
}
}
次、Silverlight の方。
なんも無し。Grid の中に Image を置くだけ。
Page.xaml
<Image x:Name="imgBackground" Stretch="Uniform" Source="MapData.png" />
え?「置いたイメージの大小によって、Image オブジェクトのサイズを変えろ」ですって?
余力があれば、チャレンジします。。。
おっと、最初に実行する前に、何でもいいので MapData.png って名前の画像ファイルを置いておいてくださいね。でないと、実行時にエラーになりますから。
投稿日時 : 2008年12月9日 22:58