как передать данные при использовании MenuItem.ItemContainerStyle - PullRequest
0 голосов
/ 17 мая 2010

Я пытался создать динамический ContextMenu, чтобы отобразить свойство имени каждого объекта в его collection of objects.
Вот конкретный пример, я подключаюсь к webservice, чтобы получить contacts и groups определенной учетной записи. Так что у меня есть такие как global variables. i display the contacts in a listbox и i want to show on right click of a contact in the listbox the list of groups that it can be added to.
чтобы иметь возможность добавить контакт в группу, мне нужен идентификатор контакта (который у меня есть) и идентификатор группы, которую я ищу, вот мой код.

xmlns:serviceAdmin="clr-namespace:MyWpfApp.serviceAdmin"
......
<ListBox.ContextMenu>
                        <ContextMenu>
                            <MenuItem Header="Refresh" Click="RefreshContact_Click"></MenuItem> 
                            <MenuItem Header="Add New Contact" Click="ContactNew_Click"></MenuItem>
                            <MenuItem Header="Add to Group" Name="groupMenus">
                                //<!--<MenuItem.Resources>
                                  //  <DataTemplate DataType="{x:Type serviceAdmin:groupInfo}" x:Key="groupMenuKey" > 
                                     //   <MenuItem>
                                     //       <TextBlock Text="{Binding name}" />
                                     //   </MenuItem>
                                   // </DataTemplate>

                               // </MenuItem.Resources>-->
                                <MenuItem.ItemContainerStyle>
                                    <Style>
                                        <Setter Property="MenuItem.Header" Value="{Binding name}"/>
                                        <Setter Property="MenuItem.Tag" Value="{Binding id}" />

                                    </Style>
                                </MenuItem.ItemContainerStyle>
                            </MenuItem>
                            <MenuItem Header="Delete Selected" Click="ContactDelete_Click"></MenuItem>
                        </ContextMenu>
                    </ListBox.ContextMenu>
                    ......

и на xaml.cs

//this code is in the method that loads the groups
loadedgroup = service.getGroups(session.key, null);
 groupListBox.ItemsSource = loadedgroup;
 groupMenus.ItemsSource = loadedgroup.ToList();

этот код показывает название групп, но мне нужен идентификатор группы, по которой щелкнули.
Если вы заметили, я прокомментировал часть кода XAML. с этим я мог связать (с легкостью) идентификатор с тегом. Но это не будет работать, и MenuItem.ItemContainerStyle - тот, который работает, но тогда я потерян:

Вопрос 1 : как создать метод-обработчик для события щелчка в подменю с именами групп?
Вопрос 2 : как мне получить идентификатор группы, по которой щелкнули, для работы?

спасибо за чтение и помогите мне в этом

Ответы [ 2 ]

2 голосов
/ 19 мая 2014

Вы можете сделать это, не используя событие Click или что-либо замысловатое.

Проблема в том, что у вашей Команды есть один DataContext (ViewModel), а у элемента есть другой. Таким образом, есть два варианта: либо поместить команду на элементы в ItemsSource (более гибкую, поскольку они могут иметь разные команды), либо выполнить привязку RelativeSource обратно к представлению, получить ViewModel через view.DataContext и получить команду из тот. Это избавит вас от лишних хлопот, передавая команду всем этим элементам данных.

Обратите внимание, что, поскольку мы делаем это с помощью стиля, мы будем привязывать одну и ту же команду к каждому элементу меню, если получим команду из некоторого DataContext, который является общим для всех пунктов меню с похожим стилем. Но так как мы сами генерируем элементы из списка похожих элементов данных, это, вероятно, то, что вы хотите. Если нет, поместите команды в элементы данных.

Если элементы в вашем ItemsSource имеют команду, это открытое свойство элемента данных, которое можно легко привязать к MenuItem.Command в вашем ItemContainerStyle. Больше C #, меньше XAML.

Это дает дополнительное преимущество - одинаково хорошо работает только с одним локализованным подменю в большой системе меню, где другие пункты меню определяются обычным способом. Вот частичная реализация списка MRU. Вы можете очень легко сделать то же самое для других аналогичных подменю - или с очень небольшим количеством дальнейшей работы, всего дерева главного меню.

Для простоты я предполагаю, что у проекта есть одно пространство имен, которое в XAML определено как local, и что этот XAML находится в представлении, называемом MainWindow.

Оба они были протестированы в полной реализации, но то, что ниже, не является полным: ради управляемого ответа SO, оно сокращено до почти минимума, чтобы XAML имел смысл. Я закончил тем, что использовал версию RelativeSource AncestorType, потому что она немного проще, и мне не нужно давать некоторым элементам списка другие команды, но я оставил другую версию закомментированной.

<Window.Resources>
    <!-- ... -->
    <DataTemplate DataType="{x:Type local:MRUListItem}" >
        <Label Content="{Binding HeaderText}" />
    </DataTemplate>
    <!-- ... -->
</Window.Resources>

<!-- ... -->

<Menu>
    <MenuItem Header="_File">
        <MenuItem Header="_Save File" 
                  Command="{Binding SaveFileCommand}" />

        <!-- ... -->
        <!-- etc. -->
        <!-- ... -->

        <MenuItem Header="_Recent Files"
                  ItemsSource="{Binding MRUList}">
            <MenuItem.ItemContainerStyle>
                <Style TargetType="MenuItem">
                    <Setter Property="Command" 
                            Value="{Binding FileOpenCommand}" />
                    <Setter Property="CommandParameter" 
                            Value="{Binding FileName}" />
                </Style>
            </MenuItem.ItemContainerStyle>
        </MenuItem>

        <!-- etc. -->
    </MenuItem>

    <!-- ... -->
</Menu>

Альтернативная версия RelativeSource, получение команды прямо из ViewModel в XAML:

<!--
            <MenuItem.ItemContainerStyle>
                <Style TargetType="MenuItem">
                    <Setter Property="Command" 
                        Value="{Binding RelativeSource={RelativeSource 
                        AncestorType={x:Type local:MainWindow}}, 
                        Path=DataContext.MRUFileOpenCommand}" />
                     <Setter Property="CommandParameter" 
                        Value="{Binding FileName}" />
                </Style>
            </MenuItem.ItemContainerStyle>
-->

C #

public class MRUList : ObservableCollection<MRUListItem>
{
    //  The owning ViewModel provides us with his FileOpenCommand
    //  initially. 
    public MRUList(ICommand fileOpenCommand)
    {
        FileOpenCommand = fileOpenCommand;
        CollectionChanged += CollectionChangedHandler;
    }

    public ICommand FileOpenCommand { get; protected set; }

    //  Methods to renumber and prune when items are added, 
    //  remove duplicates when existing item is re-added, 
    //  and to assign FileOpenCommand to each new MRUListItem. 

    //  etc. etc. etc. 
}

public class MRUListItem : INotifyPropertyChanged
{
    public ICommand FileOpenCommand { get; set; }

    private int _number;
    public int Number {
        get { return _number; }
        set
        {
            _number = value;
            OnPropertyChanged("Number");
            OnPropertyChanged("HeaderText");
        }
    }
    public String HeaderText { 
        get {
            return String.Format("_{0} {1}", Number, FileName);
        }
    }

    //  etc. etc. etc. 
}
2 голосов
/ 17 мая 2010

Ниже приведен пример использования привязки данных. Контекст данных пунктов подменю является экземпляром Group.

XAML:

<Window x:Class="MenuTest.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Height="300" Width="300">
    <Grid Background="Red">
        <Grid.ContextMenu>
            <ContextMenu>
                <MenuItem Header="Menu Item 1"></MenuItem>
                <MenuItem Header="Menu Item 2"></MenuItem>
                <MenuItem Header="SubMenu" ItemsSource="{Binding Path=Groups}"
                          Click="OnGroupMenuItemClick">
                    <MenuItem.ItemTemplate>
                        <DataTemplate>
                            <TextBlock Text="{Binding Path=Name}" />
                        </DataTemplate>
                    </MenuItem.ItemTemplate>
                </MenuItem>
            </ContextMenu>
        </Grid.ContextMenu>
    </Grid>
</Window>

Код:

using System.Collections.Generic;
using System.Windows;
using System.Windows.Controls;

namespace MenuTest
{
    public partial class Window1 : Window
    {
        public Window1()
        {
            InitializeComponent();

            Groups = new List<Group>();
            Groups.Add(new Group() { Name = "Group1", Id = 1 });
            Groups.Add(new Group() { Name = "Group2", Id = 2 });
            Groups.Add(new Group() { Name = "Group3", Id = 3 });

            DataContext = this;
        }

        public List<Group> Groups { get; set; }

        private void OnGroupMenuItemClick(object sender, RoutedEventArgs e)
        {
            MenuItem menuItem = e.OriginalSource as MenuItem;
            Group group = menuItem.DataContext as Group;
        }
    }

    public class Group
    {
        public string Name { get; set;}
        public int Id { get; set;}
    }
}
...