前々回の続き。今回のテーマは、階層メニューです。前回でなく前々回なのは、今回は前回扱った動静混在メニューは置いておくからです。題材は引き続きブラウザのブックマーク。
この記事では、実際に URL を持っていてクリックすればそのページに飛べるのをブックマーク、複数のブックマークを子要素に持てるのをフォルダと呼ぶことにします。フォルダは子要素にブックマークの他にさらにフォルダを持つことも可能とします。
まずこういうもののクラス構造を考えましょう。フォルダの下にフォルダとブックマークが混在可能なのですから、一つのコレクションで扱うために共通の基底クラスが欲しいところです。表示名 Title は共通要素ですね。ブックマークは子要素を持たない代わりに URL を保持します。となるとまずこんな感じでしょう。
// Bookmark.cs
public abstract class BookmarkBase {vw
public string Title { get; set; }
}
public class Bookmark : BookmarkBase {
public string Url { get; set; }
public Bookmark() { }
public Bookmark(string title, string url) {
this.Title = title;
this.Url = url;
}
}
public class BookmarkFolder : BookmarkBase {
public ICollection<BookmarkBase> Items { get; private set; }
public BookmarkFolder() {
this.Items = new ObservableCollection<BookmarkBase>();
}
public BookmarkFolder(string title, params BookmarkBase[] items) {
this.Title = title;
this.Items = new ObservableCollection<BookmarkBase>(items);
}
}
このクラスを使って、Windows1 の DataContext に設定します。
// App.xaml.cs
private void App_Startup(object sender, StartupEventArgs e) {
Window1 window = new Window1();
ObservableCollection<BookmarkBase> bookmarks =
new ObservableCollection<BookmarkBase>();
bookmarks.Add(new BookmarkFolder("検索エンジン",
new Bookmark("bing", "http://www.bing.com"),
new BookmarkFolder("google",
new Bookmark("google.co.jp", "http://www.google.co.jp"),
new Bookmark("google.com", "http://www.google.com")),
new BookmarkFolder("yahoo",
new Bookmark("yahoo", "http://www.yahoo.co.jp"))));
bookmarks.Add(new Bookmark("msdn", "http://msdn.microsoft.com"));
window.DataContext = bookmarks;
window.Show();
}
あとは XAML です。基本的に前々回の XAML と同じですが、ツリー状のデータをバインディングで展開するために、DisplayMemberPath だけで済ませる訳にはいきません。ItemTemplate で HierarchicalDataTemplate を使って繰り返しを表現します。
<!-- Window1.xaml -->
<!-- MenuItem の書き換え部分だけ抜粋 -->
<MenuItem Header="ブックマーク(_B)" ItemsSource="{Binding}">
<MenuItem.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding Items}">
<TextBlock Text="{Binding Title}"/>
</HierarchicalDataTemplate>
</MenuItem.ItemTemplate>
<!-- 以下は元のまま -->
まあ DisplayMemberPath の代わりに ItemTemplate 書いただけで終わりなんですが。割と見たままなんで解説もいいですよね。