使用 MenuItem.ItemContainerStyle 时如何传递数据

发布于 2024-09-02 00:34:31 字数 2532 浏览 1 评论 0原文

我一直在尝试使用动态ContextMenu来显示其对象集合中每个对象的名称属性。
这是具体的示例,我正在连接到webservice来提取特定帐户的联系人。所以我将它们作为全局变量我在列表框中显示联系人并且我想在列表框中右键单击联系人时显示可以将其添加到的组列表< /代码>。
为了能够将联系人添加到组中,我需要联系人的 id(我拥有),而我在此处查找的组的 id 是我的代码。

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();

此代码显示了组的名称,但我需要单击的组的 ID。
如果您注意到我评论了部分 xaml 代码。这样我就可以(轻松)将 id 绑定到标签。但它不起作用,MenuItem.ItemContainerStyle 是有效的,但后来我迷失了:

问题 1:我该怎么办为具有组名称的子菜单的单击事件创建处理程序方法?
问题 2:如何获取要使用的点击组 ID?

感谢您的阅读并请帮助我

i've been trying to have a dynamic ContextMenu to show the name property of each of the object in its collection of objects.
here is concrete example ,i'm connecting to a webservice to pull contacts and groups of a particular account.so i have those as global variables.i display the contacts in a listbox and i want to show on right click of a contact in the listbox the list of groups that it can be added to.
to be able to add a contact to a group i need the id of the contact(which i have) and the id of the group which i'm looking for here is my code.

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>
                    ......

and on 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();

this code is showing the name of the groups alright but i need the id of the group clicked on.
If you've noticed i commented a portion of the xaml code. with that i could bind(with ease) the id to the tag.But it won't work and the MenuItem.ItemContainerStyle is the one working but then i'm lost:

Question 1 : how do i create a handler method for a click event of a submenu that has the names of the groups?
Question 2 : how do i get the clicked group id to work with?

thanks for reading and kindly help me in this

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

痴情换悲伤 2024-09-09 00:34:31

下面是使用数据绑定的示例。子菜单项的数据上下文是 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;}
    }
}

Below is a sample using data binding. The data context of a sub menu items is an instance of 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>

Code behind:

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;}
    }
}
小ぇ时光︴ 2024-09-09 00:34:31

您可以在不使用 Click 事件或任何复杂的事情的情况下完成此操作。

问题在于您的命令有一个 DataContext(ViewModel),而该项目有另一个。因此,有两种选择:要么将命令放在 ItemsSource 中的项目上(更灵活,因为它们可以有不同的命令),要么执行相对源绑定备份到视图,通过 view.DataContext 获取 ViewModel,并从那。这可以为您节省一些将命令传递给所有这些数据项的琐碎麻烦。

请注意,由于我们是通过样式执行此操作,因此如果我们从所有类似样式的菜单项通用的某个 DataContext 获取命令,我们会将相同的命令绑定到每个菜单项。但由于我们是从类似数据项的列表中生成项目本身,这可能就是您想要的。如果没有,请将命令放在数据项上。

如果 ItemsSource 中的项目有一个命令,那么它是数据项的公共属性,可以轻松绑定到 ItemContainerStyle 中的 MenuItem.Command。更多 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>

替代的相对源版本,直接从 XAML 中的 ViewModel 获取命令:

<!--
            <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. 
}

You can do this without using the Click event, or anything convoluted at all.

The trouble is that your Command has one DataContext (the ViewModel) and the item has another. Thus, two options: Either put the command on the items in ItemsSource (more flexible, since they can have different commands), or do a RelativeSource binding back up to the view, grab the ViewModel via view.DataContext, and get the command from that. This saves you some trivial hassle passing the command around to all those data items.

Note that since we're doing this via a Style, we'll be binding the same command to each menu item if we get the command from some DataContext that's common to all the similarly styled menu items. But since we're generating the items themselves from a list of like data items, that's probably what you want. If not, put commands on the data items.

If the items in your ItemsSource have a command, that's a public property of the data item which can easily be bound to MenuItem.Command in your ItemContainerStyle. More C#, less XAML.

This has the added benefit of working equally well on just one localized submenu in a larger menu system, where other menu items are defined in the conventional way. Here's a partial MRU list implementation. You could very easily do the same thing for other similar submenus -- or with very little further work, your entire main menu tree.

For simplicity, I assume the project has one namespace that's defined as local in XAML, and that this XAML is in a view called MainWindow.

Both of these have been tested in a complete implementation, but what's below is not complete: For the sake of a manageable SO answer, it's cut down to pretty much the minimum to enable the XAML to make sense. I ended up using the RelativeSource AncestorType version because it's a bit simpler and I don't need to give some of the list items different commands, but I kept the other version commented out.

<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>

Alternative RelativeSource version, getting the command straight from the ViewModel in 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. 
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文