KatsuYuzuのブログ

.NET の開発をメインとした日記です。

Windows ストアアプリで画像ライブラリを起動して写真ファイルを選択するトリガーアクション #win8dev_jp

Windows ストアアプリで画像ライブラリを起動して写真ファイルを選択するトリガーアクション作りました。
f:id:KatsuYuzu:20140312000812p:plain
Blendでぽとぺたするだけで画像ライブラリを扱うことが出来ます。
# 今度nuget化しておきますん。
下記の昨日の記事とまったく同じ流れなので当記事では異なる部分と余談部分の続きを。

ライブラリ

2014/03/19 追記

ライブラリ化しました。
Citrus Interactions - Action / Behavior library for WinRT - Home

実装

2014/03/12 9:30 追記

errorHandleCommandないときに例外をthrowしていなかったのを修正。
errorHandleCommandをExecuteしたときにも例外をthrowしていたのを修正。

画像ライブラリ起動

画像ライブラリ起動部分の実装はとっても簡単です。FileOpenPickerクラスのPickSingleFileAsyncを呼ぶだけ!

/// <summary>
/// 写真を選択します。
/// </summary>
/// <param name="viewMode">ファイル オープン ピッカーが項目を表示するために使用する表示モード。</param>
/// <returns></returns>
private async static Task<StorageFile> PickPhotoAsync(PickerViewMode viewMode)
{
    var picker = new FileOpenPicker();

    picker.ViewMode = viewMode;

    picker.SuggestedStartLocation = PickerLocationId.PicturesLibrary;

    foreach (var extension in SupportedExtensions)
    {
        picker.FileTypeFilter.Add(extension);
    }

    return await picker.PickSingleFileAsync();
}

SupportedExtensionsはBitmapDecoderから取得します。インテリセンスの説明によるとシステムにインストールされたデコーダーから取ってくれるそうな。毎回とる必要もないのでstaticに。遅延に。

private static List<string> _supportedExtensions;

/// <summary>
/// デコーダーまたはエンコーダーによってサポートされるすべてのファイル拡張子のコレクション。
/// </summary>
private static IReadOnlyList<string> SupportedExtensions
{
    get
    {
        // 遅延初期化とする
        return _supportedExtensions 
            ?? (_supportedExtensions = BitmapDecoder.GetDecoderInformationEnumerator()
                    .SelectMany(x => x.FileExtensions)
                    .ToList());
    }
}

アクション

アクションは昨日のと同じです。

余談

警告出る部分に関してneue cc先生の記事読み漁ったところContinueWithでロギングすると。

ただ、トリガーアクションをライブラリ化した時にライブラリのユーザーは手出しできないから、エラーハンドルコマンド的なのをpublicにしようと思ったんだけど、UIスレッド外から依存関係プロパティ触ったら死んで、Dispatcher使うとWinRTには非同期のRunAsyncしかなくって、結局、それ投げっぱなしかい!って。
ここまでやって灯台モトクラシー的な感じに「あほだった……」ってきづいた。普通にエラーハンドルも最初に渡す。

StorageFile photo;
try
{
    photo = await PickPhotoAsync(viewMode);
}
catch (Exception ex)
{
    if (errorHandleCommand != null && errorHandleCommand.CanExecute(ex))
    {
        errorHandleCommand.Execute(ex);
        return;
    }
    else
    {
        throw;
    };
}

これで介入出来るし、以降の処理はあなたのコマンドの処理でしょ?ってことで、気にしていたExecute関数の呼び出し部分は変数で受けて警告消しちゃっていいねって落ち着いた。自己責任で投げっぱなし。
f:id:KatsuYuzu:20140312003702p:plain
詳細は記事末尾のgistへ。

ViewModel

昨日のと同じです。エラーハンドルコマンドを追加してとりあえずメッセージ。

private DelegateCommand<Exception> _photoErrorHandleCommand;
public DelegateCommand<Exception> PhotoErrorHandleCommand
{
    get
    {
        return this._photoErrorHandleCommand
            ?? (this._photoErrorHandleCommand = new DelegateCommand<Exception>(
                x =>
                {
                    var _ = new MessageDialog(x.Message).ShowAsync();
                }));
    }
}

XAML

ボタンなどの任意のコントロールにぽとぺた。

<Interactivity:Interaction.Behaviors>
    <Core:EventTriggerBehavior EventName="Click">
        <Behavior:PickPhotoAction CallbackCommand="{Binding SelectedPhotoCommand}"
                                  ErrorHandleCommand="{Binding PhotoErrorHandleCommand}"
                                  PickerViewMode="Thumbnail" />
    </Core:EventTriggerBehavior>
</Interactivity:Interaction.Behaviors>

エラーハンドルはアクション部分の余談の通り。

実行

f:id:KatsuYuzu:20140312003839p:plain
# プロジェクト作成から実行までの動画じゃないと伝わらない

まとめ

WinRTは簡単APIが多いってのと、非同期APIしかないってのと、両面を感じました。ぬるさくの裏側。