ListBox に項目がないときのメッセージを簡単に表示する
サンプルプロジェクト ListBox_empty_message.zip
WP7 ではソフトを開発しただけでは終わりません。最終的には AppHub にアプリを申請し通らなければ、野良アプリとして一生を終えてしまいかねません。
その AppHub 規約の中で 「表示するコンテンツが無い場合は、その旨を表示する」 というものがあるようです (Twitter で見かけた)。
たとえば ListBox 等で表示する項目が無い場合は、右の画像のようにメッセージを表示する必要があります。標準の Picture Hub などでも「同期していません。コンピュータに接続してビデオを同期させてください。」などのメッセージが表示されています。
いったいこれはどうすればいいのでしょうか?
空の場合のメッセージの表示
空のメッセージを表示する方法として、まず ListBox に表示する項目が無い場合、ListBox にダミーの項目を表示しておくという手法が考えられます。
しかし本来のテンプレートとダミー項目のテンプレート双方を用意する必要がありますし、切り替えも面倒です。
ここで紹介する方法ではコンバータを使用して、空のメッセージを表示してみます。
考え方としては、
- ListBox の下に同じ大きさの StackPanel を配置、その中の TextBlock に空のメッセージを表示しておく。
- あとは ListBox.ItemSource.Count < 1 であれば ListBox.Visibility = Collapse, StackPanel.Visibility = Visible にして表示を切り替える。
とすれば項目数に合わせて ListBox, StackPanel の表示が相互に切り替わります。
SourceItemVisibilityConverter の実装
以下は、SourceItemVisibilityConverter のソースコードです。
このコンバータは ICollection を引数(value)にとり、その格納数が 1 以上であれば、Visible を返し、それ以外なら Collapsed を返します。
つまり 表示するアイテムがあれば Visible, なければ Collapsed を返すコンバータです。
これをプロジェクトに追加してビルドしておきます。
/// <summary> /// 指定された ICollection が無ければ Collapse, あれば Visible を返す /// </summary> public class SourceItemVisibilityConverter : IValueConverter { // ListBox.Visibility にバインドされているコンバータ public virtual object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { // ICollection を取得→無ければ Collapsed ICollection collection = value as ICollection; if (collection == null) return Visibility.Collapsed; // アイテムがあれば Visible, なければ Collapsed return collection.Count < 1 ? Visibility.Collapsed : Visibility.Visible; } public virtual object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { throw new NotImplementedException(); } } /// <summary> /// SourceItemVisibilityConverter の逆 /// </summary> public class SourceItemVisibilityInverseConverter : SourceItemVisibilityConverter { // ListBox.Visibility にバインドされているコンバータ public override object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { Visibility vis = (Visibility) base.Convert(value, targetType, parameter, culture); return vis == Visibility.Visible ? Visibility.Collapsed : Visibility.Visible; } public override object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { throw new NotImplementedException(); } }
Blend でコンバータを配置する
- 次に Blend で以下のように、StackPanel, ListBox を配置します。
ListBox.ItemSource は、PhoneApplicationPage のメンバ Items が追加してあるので、それにバインドしています。
※ Blend になれていない方もいらっしゃると思いますので、バインド・コンバータの指定方法手順を書いておきます。 - ListBox の Visibility 右端の■をクリックしてデータバインドを選択します。
- 「要素プロパティ」の「シーン要素」から PhoneApplicationPage を選択し、プロパティ Items を選択します。プロパティ一覧に表示されてない場合は、ドロップダウンメニューから「すべてのプロパティ」を選択してください。
- ダイアログボックスの下矢印をクリックして、ボックスを広げます。「値コンバータ」の欄の「...」ボタンを押します。
- 追加できるコンバータの一覧が表示されますので「SourceItemVisibilityConverter」を選択します。
※ここに出てこない場合は SourceItemVisibilityConverter.cs が追加されてないか、ビルドされていない場合です。 - これで完了です。OK ボタンを押してください。
ここまでの操作で、ListBox.ItemSource のバインド元 Items の値に合わせて、ListBox が表示・非表示されるようになりました。
しかしこのままだと ListBox の下にある StackPanel が透けてしまい、右の画像のようになってしまいます。
この対処方法は、
- ListBox.Background を Transparent 以外にする
- StackPanel.Visibility に ListBox と同じくコンバータを設定する。
の2つの方法があります。
ListBox.Background を Transparent 以外にするのは、安直な方法ですがここでは StackPanel.Visibility にもコンバータを設定して表示・非表示を切り替えた方が良さそうです。
StackPanel.Visibility にコンバータを設定するには、ListBox と同じように Items のバインドからコンバータを指定します。ただしコンバータの選択では、SourceItemVisibilityConverter ではなく SourceItemVisibilityInverseConverter を選択して、ListBox とは逆の Visibility が設定されるようにしてください。
参考
Application Certification Requirements for Windows Phone