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


