使ってみよう! Live Framework

第6回 .NET Kit(3)―― ホワイトボードアプリケーション

この記事を読むのに必要な時間:およそ 6 分

Stroke情報の保存

InkCanvas.Strokesプロパティ(StrokeCollection型)のSaveメソッドを使用すると,StrokeのコレクションをISF(Ink Serialized Format)という形式で保存することができます。

Using fs = New System.IO.FileStream("strokes", IO.FileMode.Create, IO.FileAccess.Write)
    MyInkCanvas.Strokes.Save(fs)
End Using

ただ,この形式はバイナリデータですので少し工夫してStrokeコレクションをXML形式で保存できるようにします。実際に使用するコードは次のようになります。

Using ms = New System.IO.MemoryStream
    XamlWriter.Save(MyInkCanvas.Strokes, ms)
    Dim strokes = System.Text.Encoding.UTF8.GetString(ms.ToArray)
End Using

入力されたStroke情報をMeshへ追加・更新するタイミングは,InkCanvasへStrokeが追加または削除されたときとします。これらのタイミングはStrokeCollectedイベントとStrokeErasedイベントで知ることができます。

XML形式にする部分をメソッド化しMesh Objectの更新処理を追加したコードを以下に示します。

Private Sub SetStrokes()
    Using ms = New System.IO.MemoryStream
        XamlWriter.Save(MyInkCanvas.Strokes, ms)
        Dim strokes = System.Text.Encoding.UTF8.GetString(ms.ToArray)
        WhiteboardObject.Resource.SetUserData(Of String)(strokes)
    End Using
    WhiteboardObject.Update()
End Sub

Private Sub MyInkCanvas_StrokeErased(ByVal sender As Object, ByVal e As System.Windows.RoutedEventArgs) Handles MyInkCanvas.StrokeErased
    SetStrokes()
End Sub

Private Sub MyInkCanvas_StrokeCollected(ByVal sender As Object, ByVal e As System.Windows.Controls.InkCanvasStrokeCollectedEventArgs) Handles MyInkCanvas.StrokeCollected
    SetStrokes()
End Sub

クリアボタンをクリックしたときには上記のイベントは発生しないため,クリアボタンをクリックしたときの処理にもSetStrokesを呼ぶように変更しておきましょう。

以上で,ホワイトボードのStroke情報をMesh Objectへ保存できるようになりました。次は,Mesh Objectの内容からStroke情報を復元できるようにします。

Stroke情報の復元

Mesh ObjectにはXML形式の文字列としてStroke情報を保存していたので,文字列からStroke情報へ復元し,InkCanvasへ反映する作業が必要になります。以下にそのコードを示します。

Private Sub GetStrokes()
    WhiteboardObject.Load()

    Dim xml = WhiteboardObject.Resource.GetUserData(Of String)()
    If xml IsNot Nothing Then
        Dim strokes = TryCast(XamlReader.Parse(xml), StrokeCollection)
        If strokes IsNot Nothing Then
            MyInkCanvas.Strokes.Clear()
            MyInkCanvas.Strokes.Add(strokes)
        End If
    End If
End Sub

明示的にMesh ObjectのLoadメソッドを呼び,最新の内容を取得しています。そして取得した文字列の値は,XamlReader.ParseメソッドおよびTryCastを使用してStrokeCollectionに変換しInkCanvasのStrokeコレクションに追加しています。

Stroke情報を復元するタイミングはアプリケーションの起動時とMesh Objectがほかのデバイスからなど外部要因で更新された場合です。起動時に処理するにはウィンドウのLoadイベントの最後にGetStrokesメソッドの呼び出しを追加ます。Mesh Objectの更新通知を受信するには,Mesh ObjectのChangeNotificationReceivedイベントを使用します。

Private Sub WhiteboardObject_ChangeNotificationReceived(ByVal sender As Object, ByVal e As System.EventArgs) Handles WhiteboardObject.ChangeNotificationReceived
    GetStrokes()
End Sub

更新通知の受信については前回に紹介していますので,そちらも参照して,より改良してみてください。


以上で今回作成するアプリケーションは完成です。実行してみて動作を確認してみてください。アプリケーションを終了し,再度起動したら前回のホワイトボードの内容が復元されましたか? 複数の場所でアプリケーションを起動するとホワイトボードの内容が同期されましたか?

おわりに

最後に作成したアプリケーションの動作について改良点などを補足しておきます。

実際にアプリケーションを実行してみて気づいたかもしれませんが,Mesh ObjectのUpdateやLoadメソッドなどネットワークの通信部分をすべて同期呼び出ししているため,時間がかかり快適なユーザーインターフェースとは言えません。UpdateやLoadメソッドには非同期呼び出しも用意されています。たとえばUpdateの場合はUpdateAsyncメソッドがあります。

WhiteboardObject.UpdateAsync("state") ' 非同期呼び出し

非同期呼び出しをした場合Object型の引数を指定できます。アプリケーションで何らかの状態を管理する必要がある場合に利用します。非同期処理が完了を知るにはイベントを使用します。

Private Sub WhiteboardObject_UpdateCompleted(ByVal sender As Object, ByVal e As System.ComponentModel.AsyncCompletedEventArgs) Handles WhiteboardObject.UpdateCompleted
    Console.WriteLine(e.UserState)
End Sub

非同期処理が完了するまで次の非同期処理のメソッドを呼ぶことはできません。そのため,今回のアプリケーションを単純にUpdate等のメソッドを非同期用のメソッドに置き換えるだけでは動きません。その点も踏まえてぜひ改良してみてください。

また,データの同期・共有はしていますが,現在はユーザー間の共有はできません。よりおもしろいアプリケーションにするには,ユーザー間でのデータ共有が重要になってきます。この点については次回以降でふれたいと思います。

著者プロフィール

松江祐輔(まつえゆうすけ)

日本システムウエア株式会社 勤務。現在,ハードウェア設計・検証業務を担当。大学生・大学院生時代はベンチャー企業 有限会社ミレニアムシステムズにプログラマーとして従事。趣味はプログラミング。好きな言語はVisual Basic。Microsoft MVP for Windows Live Platform(Jul 2010 - Jun 2011),Windows Live(Jul 2011 - Jun 2013)。

URL:http://katamari.jp