ContextMenuへのバインディング(2of3)

前回の続きです。

MenuItemのIEnumerableをContextMenuのItemsSourceに
バインドすることで一応狙った通りに動作したのですが、
ViewModelにコントロールのMenuItemが入るのはどうも気持ち悪いので、
違う方法を考えてみます。

まず、メニュー項目を定義したPOCO(ContextMenuItemDefinitionクラス)の
IEnumerableをContextMenuのItemsSourceにバインドします。
ContextMenuItemDefinitionクラスは、とりあえず項目名(Text)を定義しておきます。

#!C#
public partial class MainPage : PhoneApplicationPage
    {
        public MainPage()
        {
            InitializeComponent();
            LayoutRoot.DataContext = new ViewModel();
        }

        private void MenuItem_Click(object sender, RoutedEventArgs e)
        {
            MenuItem menuItem = sender as MenuItem;
            return;
        }
    }

    public class ContextMenuItemDefinition
    {
        public string Text { get; set; }
    }

    public class ViewModel
    {
        public ObservableCollection<ListItem> ListItems { get; private set; }
        public ObservableCollection<ContextMenuItemDefinition> MenuDefs { get; private set; }

        public ViewModel()
        {
            this.MenuDefs = new ObservableCollection<ContextMenuItemDefinition>()
            {
                new ContextMenuItemDefinition() {Text="Menu A"},
                new ContextMenuItemDefinition() {Text="Menu B"},
                new ContextMenuItemDefinition() {Text="Menu C"},
            };

            this.ListItems = new ObservableCollection<ListItem>(){
                new ListItem() { Text="Item 1", MenuItems=MenuDefs, },
                new ListItem() { Text="Item 2", MenuItems=MenuDefs, },
                new ListItem() { Text="Item 3", MenuItems=MenuDefs, },
            };
        }
    }

    public class ListItem
    {
        public string Text { get; set; }
        public ObservableCollection<ContextMenuItemDefinition> MenuItems { get; set; }
    }

XAMLでは、ContextMenuのItemTemplateにメニュー項目の表示内容を定義します。
まず、メニューの各項目にはMenuItemが含まれるのだからと思って、次のように変更してみました(これは間違いだったのですが)。

#!XML
<Grid x:Name="LayoutRoot" Background="Transparent">
        <ListBox Grid.Row="0" x:Name="ListBox" ItemsSource="{Binding ListItems}">
            <ListBox.ItemTemplate>
                <DataTemplate>
                    <TextBlock Margin="0" TextWrapping="Wrap"
                        Text="{Binding Text}" Style="{StaticResource PhoneTextLargeStyle}">
                        <toolkit:ContextMenuService.ContextMenu>
                            <toolkit:ContextMenu ItemsSource="{Binding MenuItems}" IsZoomEnabled="False">
                                <toolkit:ContextMenu.ItemTemplate>
                                    <DataTemplate>
                                        <toolkit:MenuItem Header="{Binding Text}" Click="MenuItem_Click"/>
                                    </DataTemplate>                                    
                                </toolkit:ContextMenu.ItemTemplate>
                            </toolkit:ContextMenu>
                        </toolkit:ContextMenuService.ContextMenu>
                    </TextBlock>
                </DataTemplate>
            </ListBox.ItemTemplate>
        </ListBox>
    </Grid>

■実行結果

実行したところ、コンテキストメニューが表示されるのですが、なんか空間が空いていておかしな表示です。

実行結果

また、メニューを選択するとイベントハンドラが呼ばれるのですが、その後にコンテキストメニューが閉じられません。

実行結果

原因を調べたところ、Windows Phone ToolkiのIssueTrackerに 同じ問題が登録されていました。
その回答によると、

It's incorrect to have a MenuItem in the ContextMenu's DataTemplate.
Either one shouldn't specify MenuItems at all (and the ItemsControl base
class will create them automatically), or one should specify them via
the Items property (and they'll be used as-is). What it looks like from
the sample code is that there will be a nested MenuItem inside each real
MenuItem and I can totally imagine that interfering with the
ContextMenu's close behavior.

となっていて、MenuItemをDataTemplateのなかで使うなとあります。
そして、「MenuItemを指定せずに(データを与えて)、ItemsControlにMenuItemを生成させる」か
「Itemsプロパティを経由してMenuItemを指定して、そのまま使用させる」のどちらかとあります。

後者は前回にやったことなので、前者の方法で実装する必要があります。それを試してみた過程・結果は次の記事に回します。

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.