酢ろぐ!

カレーが嫌いなスマートフォンアプリプログラマのブログ。

Windows Phoneで任意のテキストラベルのボタンを表示するクラスを作る

Windows Phone Advent Calendar "ひとり" 2011」第26日目です。延長戦。

おそらくWindows Phoneアプリケーション開発をする上で100人居れば100人使っているだろうと思われるMessageBoxについてです。

**メッセージボックスを使う

MessageBoxクラスにはShowメソッドが提供されており、このメソッドを使用してメッセージボックスを表示します。Showメソッドはオーバーロードで2つの使い方が出来るようになっています。

***Show(String text)メソッドの場合

まず最初にメッセージボックスのメッセージ部分だけをパラメータにするタイプ。

|cs| // メッセージボックスを表示 MessageBox.Show("あなたは斧を落とす準備が出来ましたか?"); ||<

***Show(String text, String caption, MessageBoxButtons buttons)メソッドの場合

次にキャプション(タイトル)部分も含めてパラメータにするタイプ。

|cs| // メッセージボックスを表示 MessageBox.Show("あなたは斧を落とす準備が出来ましたか?", "あなたに質問です。", MessageBoxButton.OKCancel); ||<

MessageBoxクラスのメッセージボックスの使い方は以上です。ユーザーとの対話式ダイアログなのですが、回答が「ok」「キャンセル」しか用意されていません。

これではどんな斧を落としたのかユーザーに尋ねることが出来ません。どうしましょうか?

**メッセージボックスのテキストを任意のテキストにしてみる

この問題を解決するには、Microsoft.Xna.Framework.GamerServices名前空間のGuideクラスを利用します。GuideクラスにはGuide.BeginShowMessageBoxメソッドが存在しており、第3パラメータにボタンのラベルを指定することが出来ます。

|cs| private void button2_Click(object sender, RoutedEventArgs e) { // NOTE: メッセージボックスのボタンは2つまで var buttonLabels = new List(); buttonLabels.Add("金"); buttonLabels.Add("銀");

// NOTE: メッセージはnullや空文字列だとエラーが発生する
var msg = "あなたが落としたのはどちらの斧ですか?";
var title = "あなたに質問です。";

// メッセージボックスを表示
Guide.BeginShowMessageBox(title, msg, buttonLabels, 0, MessageBoxIcon.Alert, asyncCallback, null);

}

private void asyncCallback(IAsyncResult asyncResult) { // ユーザーが選択したボタンを取得する var result = Guide.EndShowMessageBox(asyncResult); } ||<

如何だったでしょうか?ユーザに斧が金色か銀色かを問い合わせることが出来るようになりました。

** (ボーナスステージ)Reactive Extensionsを使って更に使いやすく

非同期をそのまま素で触るのはイヤンな感じなのでReactive Extensions(Rx)で包みます。

BeginShowMessageBoxメソッドEndShowMessageBoxメソッドは、非同期のメソッドペアで提供されています。ソースコード内で多用すると煩雑なコードになってしまいます。Rxを使ってシンプルにしてみましょう。

button3のボタンがクリックされた時に、上記にて作成したMessageBoxExtentionsクラスを使ってメッセージボックスを表示します。

|cs| private void button3_Click(object sender, RoutedEventArgs e) { // NOTE: メッセージボックスのボタンは2つまで var buttonLabels = new List(); buttonLabels.Add("金"); buttonLabels.Add("銀");

// NOTE: メッセージはnullや空文字列だとエラーが発生する
var msg = "あなたが落としたのはどちらの斧ですか?";
var title = "あなたに質問です。";

// メッセージボックスを表示
MessageBoxExtentions.ShowObservable(title, msg, buttonLabels)
    .Subscribe(result => {
        // ユーザーが選択したボタンを取得する
    });

} ||<

今回作成したMessageBoxExtentionsクラスは以下の通りです。

BeginShowMessageBoxメソッドとEndShowMessageBoxメソッドを、ObservableクラスのFromAsyncPatternメソッドで包んでいますが、BeginShowMessageBoxメソッドのパラメータがFromAsyncPatternメソッドのパラメータに合わない為、一部パラメータをラッピングしました。

|cs| using System; using System.Collections.Generic; using Microsoft.Phone.Reactive; using Microsoft.Xna.Framework.GamerServices;

namespace MessageboxTest {

public class MessageBoxInfo {
    public MessageBoxInfo() { }
    public MessageBoxInfo(string title, string message, List<string> buttonLabels) {
        Title = title;
        Message = message;
        ButtonLabels = buttonLabels;
    }

    public string Title { get; set; }

    private string _message = null;
    public string Message {
        get { return _message; }
        set {
            if (string.IsNullOrWhiteSpace(value)) {
                throw new ArgumentException();
            }
            _message = value;
        }
    }


    private List<string> _buttonLabels = null;
    public List<string> ButtonLabels {
        get { return _buttonLabels; }
        set {
            if (value == null) {
                throw new ArgumentNullException();
            }
            if (value.Count > 2) {
                throw new ArgumentException();
            }
            _buttonLabels = value;
        }
    }
}

public static class MessageBoxExtentions {

    public static IAsyncResult BiginShowMessageBox(MessageBoxInfo info,
        MessageBoxIcon icon, AsyncCallback cb, object state) {
        return Guide.BeginShowMessageBox(info.Title, info.Message, info.ButtonLabels, 0, icon, cb, state);
    }

    public static IObservable<int?> ShowObservable(string title, string message,
        List<string> buttonLabels, MessageBoxIcon icon = MessageBoxIcon.Alert) {

        // Guide.BeginShowMessageBoxメソッドに渡すパラメータ
        var info = new MessageBoxInfo(title, message, buttonLabels);

        return Observable.FromAsyncPattern<MessageBoxInfo, MessageBoxIcon, int?>(
            MessageBoxExtentions.BiginShowMessageBox, Guide.EndShowMessageBox)(info, icon);
    }

}

} ||<

** 参照