Posts tagged ‘Silverlight Advent Calendar 2011’

こちらのエントリーはSilverlight Advent Calendar 2011の24日分の参加エントリーです。

書いていて気がつきましたが、このエントリーってクリスマスイブのエントリーなんですね(^^;

といっても、それほど特別な内容ではなくて恐縮なんですが、今回はせっかくですのでSilverlight 5に追加されたカスタムのマークアップ拡張を作成するというシナリオについてご紹介したいと思います。

まずは概要のおさらいから

マークアップ拡張とは

ご存じのとおり、SilverlightではXAMLを使ってUIを作成し、それ以外の処理はC#, VisualBasicを使ってアプリケーションを構築します。

このXAMLはご存じのとおり、XMLベースのマークアップでオブジェクトのインスタンスの宣言を行い、併せてそのオブジェクトのプロパティやイベントを設定する宣言型プログラミング言語という位置づけでした。

そのプロパティ・イベントの設定には「属性構文」「プロパティ要素構文」「コレクション構文」などがあって、設定するオブジェクトの種類などによってこれらを使い分ける必要がありました。

中でも、「属性構文」・「プロパティ要素構文」はプロパティやイベント設定の礎となる重要な構文です。

「属性構文」はXMLの属性の形でプロパティやイベントの設定が行える代わりに、その中に設定できるオブジェクトが限定されており、「文字列」もしくは「直接変換可能な値」となっていました。

20111222175611

「直接変換可能な値」とはintやdoubleなどのプリミティブ型(正確にはネイティブテキスト構文と呼ばれる構文で定義されたデータ型)や列挙型、または型コンバーターによる変換が可能な値などがこれらに当たりますが、つまりは上記以外のプロパティ設定を行う場合は必然的に「プロパティ要素構文」で記述する必要があります。

対して「プロパティ要素構文」はオブジェクトとなるXML要素のコンテント領域に[オブジェクト名].[プロパティ名]と命名された要素を内包することで柔軟なオブジェクトの設定が可能です。

20111222175850

コンテンツ領域に任意のオブジェクトタグを宣言することでプロパティに対して任意のオブジェクトを設定することが可能です。

その代り、開始タグ・終了タグのマークアップとなるため、表現が冗長となりがちで、ステップ数もかさむことから可読性の意味ではあまり優れているとは言えません。

このような理由から、多くは可能な限り属性構文で設定を行い、属性構文で設定が難しいものに関してはプロパティ要素構文を利用するという形で宣言を行います。

しかし、利用頻度の高いオブジェクトなどは、プロパティ要素構文で宣言するとステップ数が増えてしまいますので、一部のオブジェクトは属性構文で宣言できるような方法が用意されていました。

それがマークアップ拡張になります。

マークアップ拡張の利用方法

マークアップ拡張は属性構文で中かっこで指定が可能です。

20111222183451

マークアップ拡張として利用可能なクラスを中かっこ{}の中に指定し、スペースを開けてコンストラクタやプロパティの指定が行えます。 これらは,をトークンとすることで複数のパラメーターが指定可能です。

パラメーターの中に等号「=」が含まれるものがプロパティへの設定、含まれないものがコンストラクタへの設定となります。

Silverlightにはあらかじめいくつかのマークアップ拡張として利用可能なクラスが定義されています。

  • Binding:データバインディングで利用するクラス
  • StaticResource:リソースディクショナリからの参照を指定するクラス
  • TemplateBinding:コントロールテンプレート内から元となる親オブジェクトのプロパティとの通信を行うためのクラス
  • RelativeSource:バインディングやテンプレートバインディングなどで相対位置のソースを指定するためのクラス

また、XAML内の機能で利用できるマークアップ拡張としてプロパティにNull値を指定する{x:Null}があります。

Silverlight 4まで上記の定義済のマークアップ拡張を利用するのみだったのですが、Silverlight 5からは任意のマークアップ拡張を作成するというシナリオが追加されています。

マークアップ拡張は自身でカスタムのマークアップ拡張の作成方法を知ることで、上記のようなパラメーターの指定が、実際のマークアップ拡張に対してどのように作用しているのかが理解しやすくなります。

それでは早速、その方法を確認してマークアップ拡張の挙動を確認してみましょう。

カスタムのマークアップ拡張の作成方法

マークアップ拡張はMarkupExtensionクラスの派生クラスのオブジェクトになります。そのため必然的にMarkupExtensionを継承する必要があります。

MarkupExtensionはSystem.Windows.Markup名前空間にあるクラスです。

この中のProvideValueというメソッドを継承することでマークアップ拡張は作成が可能です。

using System;
using System.Windows.Markup;

namespace SLAdv2011_Day24_MarkupExtension
{
    public class MerryChristmasExtension : MarkupExtension
    {
        public override object ProvideValue(IServiceProvider serviceProvider)
        {
            return "Merry X'mas to XAMLDeveloper";
        }
    }
}

以上のコードをXAMLから呼び出す場合は、以下のようになります。

<UserControl x:Class="SLAdv2011_Day24_MarkupExtension.MainPage"

xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

xmlns:d="http://schemas.microsoft.com/expression/blend/2008"

xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"

xmlns:local="clr-namespace:SLAdv2011_Day24_MarkupExtension"

mc:Ignorable="d"

d:DesignHeight="300" d:DesignWidth="400">

<Grid x:Name="LayoutRoot" Background="DarkBlue">

<TextBlock Text="{local:MerryChristmas}"

HorizontalAlignment="Center"

VerticalAlignment="Center"

FontSize="40">

<TextBlock.Foreground>

<LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">

<GradientStop Color="#FFE0FF00" Offset="0.012" />

<GradientStop Color="#FFFF8A00" Offset="1" />

</LinearGradientBrush>

</TextBlock.Foreground>

</TextBlock>

</Grid>

</UserControl>

xmlnsにlocalの接頭辞で名前空間の宣言を行い、TextBlockコントロールのTextプロパティに対してマークアップ拡張を指定しました。

上記のように、クラス名に「~Extension」と定義することが一般的で、このExtensionに関してはXAMLでは省略が可能です。

ただし、Silverlightでは「~Extension」と名前のついていないマークアップ拡張も存在しており、、「~Extension」とすること自体は必須ではありません。

次にプロパティを定義してみます。

using System;
using System.Windows.Markup;

namespace SLAdv2011_Day24_MarkupExtension
{
    public class MerryChristmasExtension : MarkupExtension
    {
        public string To { get; set; }

        public MerryChristmasExtension()
        {
            To = "XAMLDeveloper";
        }

        public override object ProvideValue(IServiceProvider serviceProvider)
        {
            return "Merry X'mas to " + To;
        }
    }
}

 

今回はToというstring型のプロパティを1つ定義しました。

そして、XAMLではTextBlockコントロールのTextプロパティを以下のように定義してみます。

<TextBlock Text="{local:MerryChristmas To=SilverlightDeveloper}"

HorizontalAlignment="Center"

VerticalAlignment="Center"

FontSize="40">

<TextBlock.Foreground>

<LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">

<GradientStop Color="#FFE0FF00" Offset="0.012" />
<
GradientStop Color="#FFFF8A00" Offset="1" />

</LinearGradientBrush>

</TextBlock.Foreground>

</TextBlock>

すると実行結果は以下のような形となります。

MarkupExtensionImage

上記のように、マークアップ拡張そのものはとても簡単に作成できることから、利用頻度の高いユーティリティクラスなどをマークアップ拡張として定義することですっきりとしたXAMLにすることが可能です。

従来WPFでは行えたことがSilverlightでもようやく行えるようになったことで、利用方法もすっきり理解できるようになったのではないかと思います。

データの表現などはBindingでかなりの表現が行えるので、むやみにマークアップ拡張を作成することはおすすめできませんが、共通的に利用するようなユーティリティクラスを定義することで効率的な開発を行うことが可能になります。

今回のサンプルコードはこちらになります。

そんなワケでTipsというよりはどちらかというと基礎的な内容ばかりで大変恐縮です(^^;

それでは僭越ながら私のSilverlight Advent Calendar 2011のエントリーは以上になります。

みなさん、よいクリスマスをお過ごしください!

ツイートツイート
Facebook にシェア
このエントリーをはてなブックマークに追加
[`evernote` not found]

こちらのエントリーはSilverlight Advent Calendar 2011の23日分の参加エントリーです。

さて、僭越ながら3日連続でエントリーを予定している2日目で、今回のテーマは「ブラウザー外実行」に関する内容です。

ということでまずはブラウザー外実行の概要から。

ブラウザー外実行概要

一般的にSilverlightのマネージAPIで開発されたアプリケーションはご存じのとおり、Silverlightランタイム上で動作する、拡張子がxapとなっているパッケージファイルに内包されたアプリケーションです。

このxapファイルは通常HTMLやaspxなどのWebページ上にobjectタグとparamタグを使って埋め込みます。

そして、SilverlightランタイムがインストールされたクライアントマシンからWebブラウザーで、埋め込まれたページにアクセスすることでパッケージが読み込まれてランタイム上で実行されます。

20111221170905

通常は上記のようなプロセスでアプリケーションが実行されているワケです。

そこで、ブラウザーが受け持っている、Silverlightアプリの実行に必要なさまざまな機能群を切り出して、これを独立した実行ファイルにしました。

20111221171237

その実行ファイルとランタイムの組み合わせでSilverlightアプリケーションを実行を可能にした機能が本日の本題「ブラウザー外実行」になります。

実行にブラウザーを利用しないので、アプリケーションが必要としないのであれば、ネットワーク接続がないクライアント環境でも手軽にSilverlightアプリケーションを実行できる機能になります。

ただし、インストールの際にはブラウザーを使ってアプリケーションを読み出す必要がありますので、ブラウザーやネットワーク接続そのものが必要ないワケではありません。

しかし、実装そのものは大きな変更をしなくても、パッケージに含まれるマニフェストファイルの変更だけでブラウザー外実行にすることが可能なため、お手軽にクライアントアプリケーション化することができるところが特徴です。

ブラウザー外実行の設定

ブラウザー外実行は先ほどもお話した通り、xapファイルに含まれるマニフェストファイルを変更するだけで利用可能になります。 しかし、マニフェストファイルを直接メンテナンスしなくても、主要な設定項目はVisual Studio 2010のプロパティからGUI操作で簡単に設定することが可能ですので、こちらで設定することをおすすめします。

Visual Studio 2010のソリューションエクスプローラーから「Silverlightプロジェクトを右クリック→プロパティ」、もしくは「Silverlightプロジェクト内の”Properties”をダブルクリック」を行います。

20111221172004

Silverlightプロジェクトのプロパティが表示されますので、Silverlightタブにある「アプリケーションのブラウザー外実行を有効にする」をチェックするとアプリケーションがブラウザー外実行することが可能になります。

 

チェックをオンにするとすぐ下にある「ブラウザー外実行の設定」ボタンをクリックするとブラウザー外実行の主要な設定項目が格納されています。

中でも「インストールメニューの表示」にチェックが入っていないとコードによるインストール手段を用意していない限りブラウザー外実行を行うことができないので注意して下さい。

20111221172623 

ブラウザー外実行のアプリのインストール

ブラウザー外実行のアプリケーションのインストールは大きく2通りあります。

1つは「Silverlightの構成メニュー」からインストールする方法、もう1つは「コードによるインストール方法」になります。

「Silverlightの構成メニューからインストールする」の場合は、先ほどの「インストールメニューの表示」にチェックしておきます。

あとは以下のように実行中のSilverlightアプリケーション対して右クリックすることでインストールメニューが表示されます。

OutOfBrowser1

この「~をインストールします。」のメニューをクリックすることで次のようなダイアログが表示されます。

20111221174018

こちらのダイアログはインストールしたアプリケーションへのショートカットの配置先の指定となります。

スタートメニューもしくはデスクトップ上にショートカットが配置されるので、そこからアプリケーションを起動することが可能です。

ただし、上記のチェックがオンじゃなかったり、アプリケーション内で右クリックのイベントをハンドリングしている場合は構成メニューを表示させることができませんので、この方法ではインストールできません。

もう1つインストール方法は「コードからインストール」する方法になります。

コードからインストールする場合はApplicationクラスのInstallメソッドを実行するのみです。

ですので、Application.Install()でインストールが可能です。

あとはインストールしたショートカットをクリックすることがアプリケーションを起動できます。

ブラウザー外実行のアプリのアンインストール

ブラウザー外実行のアプリケーションのアンインストールも2通りあり、一つは「構成メニューからアンインストールする方法」、もう1つは「コントロールパネルからアンインストールする方法」です。

「構成メニューからのアンインストールする方法」はインストール時と同様にブラウザー外実行で実行されているSilverlightアプリケーション上で右クリックすることでアンインストールが可能です。

sloob2

ただし、インストール時と同様、アプリケーションが右クリックをハンドリングしているとこれらは表示されません。

その場合はコントロールパネルからのアンインストールを行うこととなります。

ブラウザー外実行にはまだまだ説明したいことはあるのですが、本題とは外れますのでこれくらいにしたいと思います。

ブラウザー外実行のUpdate

前段階の説明が長くてすみません(^^;

Silverlightアプリケーションは通常のWebアプリケーションと同様、サーバー上にあるアプリケーションを置き換えてしまえば新しいモジュールの配布を行うことが可能ですが、ブラウザー外実行となるとモジュールそのものがクライアント側に配置されるため、アプリケーションを自動的に更新することができません

しかし、Silverlightにはそのような時のためにアプリケーションの更新を検知する仕組みが用意されています。

Application.CheckAndDownloadUpdateAsync()

上記メソッドはApplicationクラスのメンバーですので、UIから取得する場合はApplication.Currentプロパティから実行可能です。

上記メソッドはブラウザー外実行でインストールされているアプリケーションのバージョンをチェック時、自身のモジュールのバージョンアップ版を見つけたらそのxapファイルをダウンロードして、更新します。

そのため、これらのチェックにはネットワーク通信が発生しますので、必然的にネットワーク接続がされている必要があります。

そして、ご存じのとおりSilverlightのすべての通信は非同期で実現されており、こちらも例外ではありません。そのため、それらの更新の可否を取得するために下記のイベントのイベントハンドラを指定する必要があります。

Application.CheckAndDownloadUpdateCompleted

これらをまとめると以下のようなコードになります。

(2011-12-23 22:30 コードにバグがあったので修正しました)

private void UserControl_Loaded(object sender, RoutedEventArgs e)
{
    var app = Application.Current;
    //判定および更新結果の取得完了時イベント
    app.CheckAndDownloadUpdateCompleted += app_CheckAndDownloadUpdateCompleted;
    //判定・更新
    app.CheckAndDownloadUpdateAsync();
}

 

パッケージの更新可否の確認

そして、このイベントハンドラのイベント引数を通じて、更新の可否を取得する必要があります。

更新の可否はイベント引数からUpdateAvailableというbool型のプロパティを通じて取得できます。

こちらがtrueの場合、アップデートのあったパッケージのダウンロードが成功したことを表しています。

こちらがfalseの場合はUpdateが存在しない、もしくはアップデートに失敗したということになります。

では、このアップデートの失敗はどのような場合に発生するかというと、以下のようになります。

  • Silverlightランタイムのバージョンが異なる
  • 配置されているモジュールが完全信頼モードで動作するアプリケーションなどの管理者権限を必要とするもの

上記のようなケースだと、同じくイベント引数のErrorというException型のプロパティにエラーの内容が格納されます。

Silverlightランタイムのバージョンが異なる場合はPlatformNotSupportedExceptionのインスタンスが格納されます。

既に配置されている完全信頼モードのパッケージと同じ証明書がつけられている場合は、上記のように問題なく更新できますが、証明書がなかったり、異なる証明書がついている場合は承認を必要としますのでSecurityExceptionのインスタンスが格納されます。

このあたりをまとめると以下のようなコードになります。

void app_CheckAndDownloadUpdateCompleted(object sender, CheckAndDownloadUpdateCompletedEventArgs e)
{
    if(e.UpdateAvailable)
    {
        MessageBox.Show("アップデート完了。 アプリケーションを再起動してください。");
        return;
    }
    else if(e.Error != null && e.Error is PlatformNotSupportedException)
    {
        MessageBox.Show("アップデートは存在していますが、Silverlightランタイムのバージョンが異なります。");
        return;
    }
    else if(e.Error != null && e.Error is SecurityException)
    {
        MessageBox.Show("完全信頼モードのアプリケーションで、認証が必要です");
        return;
    }
    MessageBox.Show("アップデートは存在しません。");
}

というワケで実行するとこんな感じで表示されるとおもいます。

20111222112142

アップデートを検知するために固定のWebサイトが必要だったので、今回のサンプルプログラムはWebプロジェクト入りになります。

というワケで23日のエントリーは以上になります。

今日の内容はMicrosoft Innovation Academyの「Silverlightプログラミング入門 前編」で実施しているセミナーの内容からのピックアップになりますので、よろしければ本編のセミナーもどうぞ(^^;

ツイートツイート
Facebook にシェア
このエントリーをはてなブックマークに追加
[`evernote` not found]

こちらのエントリーはSilverlight Advent Calendar 2011の22日分の参加エントリーです。

さて、僭越ながら3日連続でエントリーを予定している1日目で、今回のテーマは「データバインディング」に関する内容です。

ということでまずはデータバインディングの概要から。

データバインディング概要

Silverlightにおけるデータバインディングはドキュメントの中では以下のように説明されています。

「データバインディングはSilverlightベースのアプリケーションがデータを表示し、データと対話するための簡単な方法を提供します」

ちょっと抽象的な説明でわかりずらいですよね。

Silverlightでは一般的にはUIをXAMLで表現します。XAMLで表現されたUI要素と何かしらのソースとなるデータとの間を仲介してその内容・変化を通知するメカニズムを総称してデータバインディングと呼んでいます。

これらのメカニズムはどのような形で実現されているかというと下記の図のような形で実現されています。

Binding1

まず、Bindingクラスのオブジェクトに対して、元となるデータ「Binding Source」を指定します。

Binding Sourceには任意のオブジェクト、任意のプロパティを指定することができます。

そして、次にバインディング対象となる「Binding Target」はFrameworkElementの派生クラスの依存関係プロパティに対して指定する形になります。

FrameworkElementそのものは多くのUI要素・コントロールがその派生クラスとして構成されていますので、必然的にBindingの指定が行えるということになりますね。

つまり、上記のようにTargetとSourceの間を取り持って、そのデータの内容の通信する役割を担う存在がBindingになります。

Bindingのさまざまな機能

BindingはこのようにTargetとSource間の仲介をする際に発生するさまざまな問題解決を行う機能があります。

その中でも代表的なところでは「データ型のミスマッチ」というシナリオがあります。

ご存じのとおり、.NET環境では型に対する厳密なチェック機構があります。 これはは、特に明示的に指定しない限りはビルド時点でミスマッチが検出可能で、データ型のミスマッチが起きにくい仕組みとなっています。

しかし、XAMLでは簡潔なUIの作成を支援する目的で、自由なオブジェクト・プロパティの指定が行えるメカニズムとなっています。 つまり、こうした型のミスマッチも発生する可能性があります。

これらのミスマッチがプログラミングエラーによるものの場合は、当然デバッグする必要がありますが、意図的にミスマッチを発生させることでさまざまな機能をもたらしています。

たとえば、バインディング時に指定されたデータ型のミスマッチが発生・つまり例外が発生することで「データ検証」を行ったり、強制的にデータ型にマッチするようにコントロールを加工することで「データ表現」に役立てるというシナリオなどがあります。

その中でも今回ご紹介するのが、その「データ表現」のひとつ「値コンバーターを利用した表現」をご紹介します。

値コンバーターを利用した表現

先ほどもご紹介したように、TargetとSourceのデータ型が異なっていることが明確にわかっているのであれば、その仲介となるBindingに対して値コンバーターを指定することで強制的にデータ型のミスマッチを解決しようというメカニズムになります。

図で表現すると以下のようになります。

Binding2

TargetとSourceをBindingが仲介しているのであれば、その仲介のタイミングで専用の変換ロジックを間に挟むことでこれらのミスマッチを解決しようというメカニズムになります

それでは具体的な値コンバーターの作成方法および利用方法を見ていきましょう。

値コンバーターの作成

まず、値コンバーターはあらかじめインターフェースが定義されています。

名前空間System.Windows.DataにあるIValueConverterインターフェースを実装することで作成することができます。

IValueConverterの主要なメンバーは2つ

  • Convert [Source→Targetの場合に利用されるロジック]
  • ConvertBack[Target→Sourceの場合に利用されるロジック]

になります。

SilverlightのBindingのModeプロパティ(データフローを指定するプロパティ)は既定ではOneWayとなりますので、バインディングに対して明示的にTwoWayバインディングを指定しない限り、ConvertBackは利用されません。

いずれも戻り値・引数の型などは同じになります。

Object Convert(
    Object value, //ソースもしくはターゲットの値
    Type targetType, //ソースもしくはターゲットの型
    Object parameter, //Bindingで指定されたパラメーター
    CultureInfo culture ) //カルチャー情報

値コンバーターの指定方法、サンプル

というワケで値コンバーターの指定およびサンプルコードになります。

まず、値コンバーターから

using System;
using System.Windows.Media;
using System.Windows.Data;

namespace SLAdv2011_Day22_Converter
{
    public class DoubleBrushConverter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            var source = System.Convert.ToInt32(value);

            var brush = new SolidColorBrush();

            if(source < 20)         brush.Color = Colors.Blue;
            else if(source < 40)    brush.Color = Colors.Green;
            else if(source < 60)    brush.Color = Colors.Yellow;
            else                    brush.Color = Colors.Red;

            return brush;
        }

        //今回は実装なし
        public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            throw new NotImplementedException();
        }
    }
}

 

取得したDouble値を強制的にintに変換して、値によって異なる色のBrushオブジェクトに変換する値コンバーターです。

今回はサンプルですのでConvertBackは実装していません。

続いて、このコンバーターを利用したサンプルです。

<UserControl x:Class="SLAdv2011_Day22_Converter.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:local="clr-namespace:SLAdv2011_Day22_Converter"
    mc:Ignorable="d"
    d:DesignHeight="300" d:DesignWidth="400">
    <UserControl.Resources>
        <!--コンバーターはリソースディクショナリへ-->
        <local:DoubleBrushConverter x:Key="DBConverter" />
    </UserControl.Resources>
        
    <StackPanel>
        <Slider Name="scoreSlider" Minimum="1" />
        <StackPanel Orientation="Horizontal">
            <!--値コンバーターを利用したインジケーター-->
            <Ellipse Margin="5" Width="15" Height="15" 
                     Fill="{Binding ElementName=scoreSlider, Path=Value, Converter={StaticResource DBConverter}}" />
            <TextBlock Text="{Binding Value,ElementName=scoreSlider}" 
                     VerticalAlignment="Center" />
        </StackPanel>
    </StackPanel>

</UserControl>

今回ははUserControlのリソースディクショナリへ値コンバーターを格納し、Sliderで指定された値をEllipseを使ったインジケーターとして利用しています。

ご存じのとおり、EllipseのFillプロパティはBrush型のプロパティで、SliderのValueプロパティはDouble型のプロパティです。 こうした異なるミスマッチしたデータ型のバインディングで値コンバーターを指定することで別の新しい表現につなげることも可能です。

サンプルコードはこちら。

というワケで今日は以上になります。

今日の内容はMicrosoft Innovation Academyの「Silverlightプログラミング入門 前編」で実施しているセミナーの内容からのピックアップになりますので、よろしければ本編のセミナーもどうぞ(^^;

ツイートツイート
Facebook にシェア
このエントリーをはてなブックマークに追加
[`evernote` not found]