locked
複数のSilverlightプラグインのダウンロード状況を1つのスプラッシュスクリーンで表示する方法 RRS feed

  • 質問

  • いつもお世話になってます。

    この度、複数のSilverlightプラグインのダウンロード状況を1つのスプラッシュスクリーンで表示させる方法がどうしても分からず質問をさせて頂きました。

    現在、HTML上に3つのSilverlightプラグインを配置しております。
    標準ではページ起動時に、各Silverlightプラグイン毎にスプラッシュスクリーンが表示されるかと思いますが、この3つ表示されるスプラッシュスクリーンを1つのスプラッシュスクリーンに集約してダウンロード状況をアナウンスしたいと考えております。

    スプラッシュスクリーンの表示領域はSilverlightプラグインの領域となる為、起動時には1つのSilverlightプラグインサイズを画面最大としてスプラッシュスクリーンを表示し、全Silverlightプラグインのダウンロード完了後にHTMLブリッジを使用して各Silverlightプラグインのサイズを変更しようと考えております。

    3つのSilverlightプラグインは1つにする事は考えておりません。

    Downloaderオブジェクトを使用して実現可能なのか調査しておりましたが、中々解決に至るサンプルや解説等も見つからずにいます。
    解決策を知っておられる方がおりましたら、どうかご教授の程お願い申し上げます。




    ★良い回答には回答済みマークを付けよう! kazuto Blog : http://blogs.wankuma.com/kzt/
    2010年3月4日 14:40

回答

  • 沢山要件があるようなので、1つづつ回答しやすいものからです。

    非同期に、DLLをダウンロードし、そのDLL内にあるPageをフレームに表示する場合は、次のようなコードでできます。
    MyClassLibraryのローカルコピーをFalseにしておく必要があります。

    Imports System.Runtime.CompilerServices
    Imports MyClassLibrary

    Partial Public Class MainPage
        Inherits UserControl

        Public Sub New()
            InitializeComponent()

            'このメソッドを呼ぶ契機はなんでも良い
            DownLoadStart()
        End Sub

        Public Sub DownLoadStart()

            Dim wc As New WebClient

            AddHandler wc.OpenReadCompleted, AddressOf wc_OpenReadCompleted
            AddHandler wc.DownloadProgressChanged, AddressOf wc_DownloadProgressChangedEvent

            wc.OpenReadAsync(New Uri("./MyClassLibrary.dll", UriKind.Relative))

        End Sub
        Private Sub wc_OpenReadCompleted(ByVal sender As Object, ByVal e As OpenReadCompletedEventArgs)

            If e.Error Is Nothing Then
                DownloadLog.Items.Add("Download Complete")
                Dim part As AssemblyPart = New AssemblyPart
                part.Load(e.Result)
                ShowPage1()
                'ここでも完了状態をHTMLブリッジなどを経由して渡す。
            Else
                DownloadLog.Items.Add(e.Error.Message)
                DownloadLog.Items.Add(e.Error.InnerException.Message)
                'ここでも完了状態をHTMLブリッジなどを経由して渡す。
            End If

        End Sub

        <MethodImpl(MethodImplOptions.NoInlining)> _
        Private Sub ShowPage1()

            'MyClassLibraryの中にあるPage1 をFrameControlに表示する。
            Dim cp As New Page1
            Frame01.Content = cp

        End Sub

        Private Sub wc_DownloadProgressChangedEvent(ByVal sender As Object, ByVal e As DownloadProgressChangedEventArgs)

            DownloadLog.Items.Add(e.ProgressPercentage.ToString)

            'ここで、他のプラグインとの強調動作の為に
            'HTMLブリッジやメッセージングでデータをやり取りするとよさそう

        End Sub

    End Class


    K.Oumi
    • 回答としてマーク 和人 2010年3月12日 5:24
    2010年3月9日 5:23
  • こんなXAMLです。

    <UserControl
        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"
        mc:Ignorable="d" xmlns:navigation="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Navigation" x:Class="DownLoader.MainPage"
        d:DesignWidth="640" d:DesignHeight="480">
     <Grid x:Name="LayoutRoot" Width="600">
      <Grid.RowDefinitions>
       <RowDefinition Height="24"/>
       <RowDefinition Height="264"/>
       <RowDefinition Height="192"/>
      </Grid.RowDefinitions>
      <TextBlock Text="Downloader" HorizontalAlignment="Left" VerticalAlignment="Center"/>
      <ListBox x:Name="DownloadLog"  Margin="0" Grid.Row="2"/>
      <navigation:Frame x:Name="Frame01" Margin="0" Grid.Row="1"/>
     </Grid>
    </UserControl>

    K.Oumi
    • 回答としてマーク 和人 2010年3月12日 5:24
    2010年3月9日 5:55
  • 複数プラグインの起動時の強調動作について

    ※生XAMLを利用する場合は、既にアーキテクチャをそうしてしまったということで、ちょっと良い方法は思いつきません。
    ※以下は、生XAMLを使わずに、自前のスプラッシュ用プラグインを用意する場合の話です。


    この場合問題となるのは、スプラッシュ用プラグインがロードされていないのに、他のプラグインから一生懸命状況を通知する場合です。相手がいないので問題です。
    これを回避するには(1つの案ですが)

    どのプラグインも、実態をダウンロードしないようにしておいて、スプラッシュプラグインからのトリガーを待ち受けるようにしておきます。(つまり、からっぽのページが表示されるだけといったイメージです)

    スプラッシュプラグインは、自分自身の準備が終わったら、他のプラグインへそれぞれの実態をダウンロードするようにトリガします。
    トリガーは、メッセージングでも、JavaScrupt経由でのSilverlightメソッド呼び出しでも、どちらでも良いと思います。
    (ここで、各プラグインが動いていない可能性があるので、リトライする仕組みを実装しておく必要があります。)


    各プラグインは、自分自身のメインライブラリをダウンロードします。
    状況を、メッセージングか、JavaScript経由でスプラッシュプラグインへ通知します。
    JavaScriptを利用する場合は、

    ・各種プラグインからJavaScriptのメソッドを呼び出し。
    ・JavaScriptのメソッドからスプラッシュスクリーンのプロパティへ値をセット
    ・スプラッシュが状況判断
    スプラッシュプラグインは、状況を統合して表示できます。

    ざっとこのようなシナリオで良いのではないかと思うのですが。
    何かと面倒な感じですね・・・


    他にも実装方法はあると思います。


    K.Oumi
    • 回答としてマーク 和人 2010年3月12日 5:24
    2010年3月9日 6:23

すべての返信

  • 試してみてないので、妄想になっちゃうかもしれませんが。

    バックグランドワーカーとWebClient でうまいことやるってできないかなぁ・・・

    WebClient でAssemblyを読み込むって感じで…

        Dim assemblyPart As New AssemblyPart()

        Dim assembly As Assembly = assemblyPart.Load(e.Result)

        Dim myClass As Object = assembly.CreateInstance("MyClassLibrary.MyClass")


    こんな感じの事をバックグランドワーカーで出来ないかなと・・・
    進行状況は、バックグラウンドワーカーのProgressChangedEventで…

    試した事は無いので、外していたらごめんなさい。



    K.Oumi
    2010年3月5日 1:07
  • K.Oumi様

    回答有難う御座います。

    今回使用したいスプラッシュスクリーンは、HTML内で定義している、SilverlightプラグインオブジェクトTagで指定しているスプラッシュスクリーン(XAML)を使用しようと考えています。
    よって、スプラッシュスクリーンに表示するダウンロード進捗数はJavaScriptで更新する事となるかと思っています。

    Silverlightでのカスタマイズしたスプラッシュスクリーンは既に作成済みなのですが、複数のSilverlightプラグインのダウンロード状況を統合する方法が見当たらずにいます。



    ★良い回答には回答済みマークを付けよう! kazuto Blog : http://blogs.wankuma.com/kzt/
    2010年3月5日 1:27
  • あ~3つって、そういうことでしたか、勘違い・・・申し訳ない。
    単純に状況がマージされる方法は思いつかないです(^^;


    それぞれのプラグインで、非同期にダウンロードを実行するようにして、1つのプラグインに、ダウンロード状況をメッセージング(LocalMessaging)して、スプラシュは1つだけっていう方法とかしか、思いつかない・・・
    でも、これだと、それぞれのプラグインから、JavaScriptをHTMLブリッジで呼び出してって事でも同じか・・・
    ダウンロードが失敗したときの実装もちょっと面倒

    役立たずで申し訳ない。


    K.Oumi
    2010年3月5日 2:27
  • K.Oumi様

    分かりづらい説明をしてしまい申し訳御座いませんでした。

    標準ではHTMLにObjectタグを使用してSilverlightプラグインの記述をすれば、スプラッシュスクリーンの表示⇒Xapのダウンロードと自動的にやってくれますが、その実装を独自にやれれば実現可能なのかと考えております。

    Silverlightプラグインは内部的にDownloaderオブジェクトを使用してXapのダウンロード等をしているのかと思っていますが、Downloaderオブジェクトでスプラッシュスクリーンの表示からXapのダウンロードまでの一連の実装がどのようにするのか分かっておりません。(Downloaderオブジェクトでファイルをダウンロードするサンプルなどは見かけますが)

    またはDownloaderオブジェクトを使用すると言う事も見当違いなのかもしれませんが・・。

    ★良い回答には回答済みマークを付けよう! kazuto Blog : http://blogs.wankuma.com/kzt/
    2010年3月5日 2:52
  • >標準ではHTMLにObjectタグを使用してSilverlightプラグインの記述をすれば、スプラッシュスクリーンの表示⇒Xapのダウンロードと自動的にやってくれますが、その実装を独自にやれれば実現可能なのかと考えております。

    この方法を最初に言ったつもりだったんですが、ちょっと説明不足でしたね。

    趣旨は、ダウンロード進行状況を1つの状況表示で済ませたいという事、と思います。

    であれば、各プラグインで独自にダウンロードし、その進捗状況を集約できれば良いということになります。
    Silverlight のスプラッシュスクリーン機能?では単一のリソースのダウンロードに対する状況しか把握できないように考えています。

    また、ブラウザレベルで、各Silverlightのインスタンスを統合する事は出来ない(はず)と思っています。であれば、各プラグインが強調動作する方法を探すしかないと思います。
    つまり、LocalMessaginやHTMLブリッジによって、状況を統合するという事です。

    ダウンロードを各Silverlightインスタンスで独自に行えば、メッセージングを行う事をコントロールできるようになります。

    各インスタンスの立ちあがりを極力早くすませ、必要なアセンブリをバックグラウンドでダウンロードすれば、進捗状況をトラップできます。これをLocalMessagingなどで、スプラッシュ用のインスタンスに送信すれば良いのではないでしょうか。

    ですので、

    アセンブリパートのロードをバックグランドワーカで行い、という事でいけるのではないかなぁと思ったのです。試してはいません・・・
    以下が役に立つ情報と思います。

    Silverlight での開発に関する 3 つの重要なヒント
    http://msdn.microsoft.com/ja-jp/magazine/dd483293.aspx

    BackgroundWorker クラス
    http://msdn.microsoft.com/ja-jp/library/system.componentmodel.backgroundworker(VS.95).aspx

    実際に組み合わせて試した事はありません。
    バックグラウンドワーカーで登録するProgressChangedイベントの中でLocalMessaginが行えるかは試していません。

    Silverlight ベースのローカル アプリケーション間の通信
    http://msdn.microsoft.com/ja-jp/library/dd833063(VS.95).aspx
    方法: Silverlight ベースのローカル アプリケーション間の通信を実装する
    http://msdn.microsoft.com/ja-jp/library/dd833075(VS.95).aspx
    LocalMessageSender クラス
    http://msdn.microsoft.com/ja-jp/library/system.windows.messaging.localmessagesender(VS.95).aspx
    LocalMessageReceiver クラス
    http://msdn.microsoft.com/ja-jp/library/system.windows.messaging.localmessagereceiver(VS.95).aspx

    試してないネタで申し訳ないです。


    K.Oumi
    2010年3月8日 6:29
  • ちょっとだけ試してみたのですが、LocalMesagingでの強調動作は、開始がボタンなどのユーザ起因だとまだしも、コンストラクタやLoadedの延長だと、レシーバ側が起動しているという保証がないので、いまいちでした。
    これを回避するコードを書くのもうーん・・・orz

    それと、Assemblyのロードだと、DownloadProgressChangedイベントがあるので、バックグラウンドワーカは必要ない気がしてきました。 orz


    時間が作れたら、もう少し書いてみようと思いますが、どうやら、WebClientとHTMLブリッジだけで上手くコントロールできそうな気がします。

    K.Oumi
    2010年3月9日 0:46
  • K.Oumi様

    詳細に返信を頂きまして有難う御座いました。
    その後、私の方でも色々と検討しておりますが、依然として解決策は見つかっておりません。

    整理する為にも改めて今回の実現したい事をまとめます。

    HTML上には複数のSilverlightプラグイン(Xap)を配置する予定であり、各Silverligプラグインのダウンロード状況を一つのスプラッシュスクリーンで集約したい。
    説明で漏れている部分が御座いまして、使用するスプラッシュスクリーンはSilverlightプラグインのObjectタグ内、paramタグのSplashScreenSourceに指定したWebサーバー側に配置している生のXAMLを使用したいと思っております。

    K.Oumi様から頂いたAssemblyのロードを行うロジックは何処の場所で行い、どのタイミングで行うのか私自身理解が出来ておりません。
    私の考えでは、HTMLに埋め込んであるSilverlightプラグイン(Xap)は、自身のタイミングでダウンロード出来るものなのでしょうか?

    各Silverlightプラグインが読み込み終了後ではローカルメッセージやHTMLブリッジを使用して各Silverlightプラグインでやり取りが行えると思いますが、Silverlighプラグインの読み込み前の話になるかと思っていますので、ローカルメッセージは使用できないと考えております。

    現在は回避策として以下のような実装をしております。

    1:HTMLに一つだけSilverlightプラグインを埋め込んでおき、画面全体表示にしておく。
    2:埋め込んだSilverlightプラグインの起動UserControlの、Loadedイベントで他のSilverlightプラグインをSilverlight.jsのcreateObject関数を使用して生成してHTMLブリッジを使用し  てHTMLに埋め込む。(その際にLocalMessageReceiverにて他SilverlightプラグインからのメッセージをListenして待機しておく)
    3:createObject関数にて作成された各Silverlightプラグインの起動UserControlのLoadedイベントにて、初期に埋め込んだSilverlightプラグインに対してLocalMessageSenderを使用  して起動終了通知を送る。
    4:全Silverlightプラグインの起動終了通知を受けた後、HTMLブリッジを使用して画面の配置成形を行う。

    現在はこのような実装を行っておりますが、依然とダウンロード状況の集約は行えておりません。
    また、ローカルメッセージは非同期で行われ、SendAsyncメソッドの呼び出し順序の保障はされておりませんので、複数のSilverlightプラグインの起動時のインタラクション等は上手く実装出来ておりません。

    複数のSilverlightプラグインを使用するのは色々と検討すべき事が多いと今回感じております。

    今回なぜSilverlightプラグインを複数に分けたと言いますと、あるSilverlightプラグインはHTMLブリッジの操作によって、HTML要素を表示したり非表示にしたりする実装を考えておりまして、1つのSilverlightプラグインですと特定の領域にHTMLを表示する為にはSilverlightプラグインの上部に重ねるような実装を行わなければならず、その実装が採用出来ないという理由があります。
    重ねるなら、Silverlightプラグインの表示領域をHTMLブリッジで見えないようにし、Silverlightプラグインの下層にあるHTMLを見せるようにするという方針となっています。

    色々と検討すべき事が多く、頭を悩ませておりますが現在も上手く実装が行えないか調査中で御座います。
    頂いた情報を十分に使用させて頂き、調査を行わせて頂きます。



    ★良い回答には回答済みマークを付けよう! kazuto Blog : http://blogs.wankuma.com/kzt/
    2010年3月9日 3:22
  • 沢山要件があるようなので、1つづつ回答しやすいものからです。

    非同期に、DLLをダウンロードし、そのDLL内にあるPageをフレームに表示する場合は、次のようなコードでできます。
    MyClassLibraryのローカルコピーをFalseにしておく必要があります。

    Imports System.Runtime.CompilerServices
    Imports MyClassLibrary

    Partial Public Class MainPage
        Inherits UserControl

        Public Sub New()
            InitializeComponent()

            'このメソッドを呼ぶ契機はなんでも良い
            DownLoadStart()
        End Sub

        Public Sub DownLoadStart()

            Dim wc As New WebClient

            AddHandler wc.OpenReadCompleted, AddressOf wc_OpenReadCompleted
            AddHandler wc.DownloadProgressChanged, AddressOf wc_DownloadProgressChangedEvent

            wc.OpenReadAsync(New Uri("./MyClassLibrary.dll", UriKind.Relative))

        End Sub
        Private Sub wc_OpenReadCompleted(ByVal sender As Object, ByVal e As OpenReadCompletedEventArgs)

            If e.Error Is Nothing Then
                DownloadLog.Items.Add("Download Complete")
                Dim part As AssemblyPart = New AssemblyPart
                part.Load(e.Result)
                ShowPage1()
                'ここでも完了状態をHTMLブリッジなどを経由して渡す。
            Else
                DownloadLog.Items.Add(e.Error.Message)
                DownloadLog.Items.Add(e.Error.InnerException.Message)
                'ここでも完了状態をHTMLブリッジなどを経由して渡す。
            End If

        End Sub

        <MethodImpl(MethodImplOptions.NoInlining)> _
        Private Sub ShowPage1()

            'MyClassLibraryの中にあるPage1 をFrameControlに表示する。
            Dim cp As New Page1
            Frame01.Content = cp

        End Sub

        Private Sub wc_DownloadProgressChangedEvent(ByVal sender As Object, ByVal e As DownloadProgressChangedEventArgs)

            DownloadLog.Items.Add(e.ProgressPercentage.ToString)

            'ここで、他のプラグインとの強調動作の為に
            'HTMLブリッジやメッセージングでデータをやり取りするとよさそう

        End Sub

    End Class


    K.Oumi
    • 回答としてマーク 和人 2010年3月12日 5:24
    2010年3月9日 5:23
  • こんなXAMLです。

    <UserControl
        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"
        mc:Ignorable="d" xmlns:navigation="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Navigation" x:Class="DownLoader.MainPage"
        d:DesignWidth="640" d:DesignHeight="480">
     <Grid x:Name="LayoutRoot" Width="600">
      <Grid.RowDefinitions>
       <RowDefinition Height="24"/>
       <RowDefinition Height="264"/>
       <RowDefinition Height="192"/>
      </Grid.RowDefinitions>
      <TextBlock Text="Downloader" HorizontalAlignment="Left" VerticalAlignment="Center"/>
      <ListBox x:Name="DownloadLog"  Margin="0" Grid.Row="2"/>
      <navigation:Frame x:Name="Frame01" Margin="0" Grid.Row="1"/>
     </Grid>
    </UserControl>

    K.Oumi
    • 回答としてマーク 和人 2010年3月12日 5:24
    2010年3月9日 5:55
  • 複数プラグインの起動時の強調動作について

    ※生XAMLを利用する場合は、既にアーキテクチャをそうしてしまったということで、ちょっと良い方法は思いつきません。
    ※以下は、生XAMLを使わずに、自前のスプラッシュ用プラグインを用意する場合の話です。


    この場合問題となるのは、スプラッシュ用プラグインがロードされていないのに、他のプラグインから一生懸命状況を通知する場合です。相手がいないので問題です。
    これを回避するには(1つの案ですが)

    どのプラグインも、実態をダウンロードしないようにしておいて、スプラッシュプラグインからのトリガーを待ち受けるようにしておきます。(つまり、からっぽのページが表示されるだけといったイメージです)

    スプラッシュプラグインは、自分自身の準備が終わったら、他のプラグインへそれぞれの実態をダウンロードするようにトリガします。
    トリガーは、メッセージングでも、JavaScrupt経由でのSilverlightメソッド呼び出しでも、どちらでも良いと思います。
    (ここで、各プラグインが動いていない可能性があるので、リトライする仕組みを実装しておく必要があります。)


    各プラグインは、自分自身のメインライブラリをダウンロードします。
    状況を、メッセージングか、JavaScript経由でスプラッシュプラグインへ通知します。
    JavaScriptを利用する場合は、

    ・各種プラグインからJavaScriptのメソッドを呼び出し。
    ・JavaScriptのメソッドからスプラッシュスクリーンのプロパティへ値をセット
    ・スプラッシュが状況判断
    スプラッシュプラグインは、状況を統合して表示できます。

    ざっとこのようなシナリオで良いのではないかと思うのですが。
    何かと面倒な感じですね・・・


    他にも実装方法はあると思います。


    K.Oumi
    • 回答としてマーク 和人 2010年3月12日 5:24
    2010年3月9日 6:23
  • 最後の問題

    >特定の領域にHTMLを表示する為にはSilverlightプラグインの上部に重ねるような実装を行わなければならず、

    これは、なぜ?を問うてもダメだと思うので、うーん、どうやってもSilverlightは前面にきちゃうからどうしようもないのか・・・

    HTML自体をXAMLで作成し、動的に、表示したりしなかったりという逆転の発想でなんとかできると良いのですけど・・・


    以上、的外れな事も多々あったかもしれません。ご容赦。
    K.Oumi
    • 編集済み K.Oumi 2010年3月9日 6:49 漢字間違い
    2010年3月9日 6:49
  • K.Oumi様

    詳細なサンプル例まで教えて頂き本当に有難うございます。
    今回頂いたサンプルでWebClientを使用したダウンロードの実装が分かりました。

    Webサーバーに配置している生のXAMLを使用する事に執着しており、そのような実装方法がある事を気付かずにいました・・。
    初期ロードは空っぽの軽量Silverlightプラグインを読み込ませ、その後にお手製のスプラッシュスクリーンを起動すると言った事なのですね。
    とても理にかなっており、実装をそのように変更して為してみたいと思います。

    今回、なぜHTMLなのかと言いますと、初めはHTMLをXAMLに変換し情報を表示していたのですが、表示文字数が膨大になるとどうしても現行のSilverlightではスクロール等でレスポンスが著しく重くなったりしていたので、膨大な文字についてはHTMLで表示するといった対応になった次第であります。

    Silverlight4のWebBrowserを期待していたのですが、Out of Browserでしか使用が出来ないようなのでお手製のHTMLビューアー的な物を実装する事となりました。
    早速教えて頂いたサンプルを組んでみます。

    解決となりましたら再度この場でご報告させて頂きます。

    又、他の実装例等が御座いましたらご教授お願い致します。

    ★良い回答には回答済みマークを付けよう! kazuto Blog : http://blogs.wankuma.com/kzt/
    2010年3月9日 7:23
  • K.Oumi様

    その後実装の方を行いまして、無事実現する事が出来ました。

    ダウンロード状況の進捗通信ではローカルメッセージを使用した場合と、ScriptObjectとScriptableType属性を使用したJavascriptの2つの方法を試しました。
    Javascriptを使用した場合の方が安定しており、かつ進捗も若干速いように感じております。
    ローカルメッセージの場合ですと、通信の例外発生(悪くタイミングがずれると)が度々おこり、そのつど再起処理を行う必要がありました。又、LocalMessageSenderとLocalMessageReceiverの実装もコード量が増えてしまう為、現在はJavascript方式を採用しております。

    最終的な課題として、各Silverlightプラグインがそれぞれメインアセンブリをダウンロードする為、ダウンロード進捗の合間に時間があいてスムーズなダウンロード進捗はおこなえておりませんが、当初の要件は無事満たせました。

    K.Oumi様に詳細なサンプルコードや説明を頂いたおかげです。
    本当にありがとうございました。

    又、何かありましたら宜しくお願い致します。

    ★良い回答には回答済みマークを付けよう! kazuto Blog : http://blogs.wankuma.com/kzt/
    2010年3月12日 5:24