WPF の MenuItem をバンディングで表現してみようのコーナー。ブラウザのブックマークをサンプルにしてみます。
まずはブックマークを表す VM のクラスを用意。
// Bookmark.cs
public class Bookmark { // INotifyPropertyChanged は略
public string Title { get; set; }
public string Url { get; set; }
public Bookmark(string title, string url) {
this.Title = title;
this.Url = url;
}
}
これを元に、メニューの XAML を記述します。
<!-- Window1.xaml -->
<Window x:Class="Sample.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<DockPanel>
<Menu DockPanel.Dock="Top">
<MenuItem Header="ブックマーク(_B)"
ItemsSource="{Binding}" DisplayMemberPath="Title"/>
</Menu>
<WebBrowser x:Name="browser"/>
</DockPanel>
</Window>
あとは DataContext に Bookmark のコレクションを設定します。Application の Startup でやるようにしておきます。
// App.xaml.cs
private void App_Startup(object sender, StartupEventArgs e) {
Window1 window = new Window1();
ObservableCollection<Bookmark> bookmarks =
new ObservableCollection<Bookmark>();
bookmarks.Add(new Bookmark("bing", "http://www.bing.com"));
bookmarks.Add(new Bookmark("google", "http://www.google.co.jp"));
bookmarks.Add(new Bookmark("yahoo", "http://www.yahoo.co.jp"));
window.DataContext = bookmarks;
window.Show();
}
ここまでは簡単だと思います。
さて、これで取り敢えずメニューは表示されるようになりますが、当然項目をクリックしても何の反応もありません。メニュー項目をクリックしたときにその URL に移動する動作が必要です。
入力への応答の記述方法はいくつかありますが、今回は CommandBinding を使用します。
<!-- Window1.xaml -->
<!-- ブックマークのメニューの部分だけ書き換え -->
<MenuItem Header="ブックマーク(_B)"
ItemsSource="{Binding}" DisplayMemberPath="Title">
<MenuItem.CommandBindings>
<CommandBinding Command="Favorites" Exected="Navigate"/>
</MenuItem.CommandBindings>
<MenuItem.ItemContainerStyle>
<Style TargetType="{x:Type MenuItem}">
<Setter Property="Command" Value="Favorites"/>
<Setter Property="CommandParameter" Value="{Binding}"/>
</Style>
</MenuItem.ItemContainerStyle>
</MenuItem>
NavigationCommands.Favorites は基本ライブラリが定義している RoutedCommand であり、Routed の名前通り、コマンドが呼び出されると要素ツリー内の CommandBinding を検索してくれます。つまり、親の MenuItem の CommandBindings に Favorites を設定しておけば子 MenuItem の全ての Command を拾ってくれるって事です。ショートカットキー「Ctrl + I」が定義されているのが難点ですが、ここではおいておきます。
ItemContainerStyle は、ItemsSource を元に作られる各子 MenuItem 自体の Style です。
Favorites コマンドで実行されるメソッド(Navigate)内でどのアイテムがクリックされたか判定するために、CommandParameter を使います。子 MenuItem の DataContext は Bookmark オブジェクトになるため、この DataContext そのものである Bookmark オブジェクトがパラメータになります。
あとは CommandBinding.Executed イベントで呼び出される Navigate メソッドです。
// Window1.xaml.cs
private void Navigate(object sender, ExectedRoutedEventArgs e) {
Bookmark bm = e.Parameter as Bookmark;
if (bm != null) {
this.browser.Navigate(new Uri(bm.Url));
}
}
次のステップは CompositeCollection を使う場合 および 階層メニューを表示する場合。