How do I handle click events in a data bound menu

2020-08-19 05:38发布

问题:

I've got a MenuItem whos ItemsSource is databound to a simple list of strings, its showing correctly, but I'm struggling to see how I can handle click events for them!

Here's a simple app that demonstrates it:

<Window x:Class="WPFDataBoundMenu.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" Height="300" Width="300">
<Grid>
    <Menu>
        <MenuItem Header="File" Click="MenuItem_Click" />
        <MenuItem Header="My Items" ItemsSource="{Binding Path=MyMenuItems}" />
    </Menu>
</Grid>

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

namespace WPFDataBoundMenu
{
    /// <summary>
    /// Interaction logic for Window1.xaml
    /// </summary>
    public partial class Window1 : Window
    {
        public List<string> MyMenuItems { get; set;}

        public Window1()
        {
            InitializeComponent();
            MyMenuItems = new List<string> { "Item 1", "Item 2", "Item 3" };
            DataContext = this;
        }

        private void MenuItem_Click(object sender, RoutedEventArgs e)
        {
            MessageBox.Show("how do i handle the other clicks?!");
        }
    }
}

Many thanks!

Chris.

回答1:

<MenuItem Header="My Items" ItemsSource="{Binding Path=MyMenuItems}" Click="DataBoundMenuItem_Click" />

Code behind..

private void DataBoundMenuItem_Click(object sender, RoutedEventArgs e)
{
   MenuItem obMenuItem = e.OriginalSource as MenuItem;
   MessageBox.Show( String.Format("{0} just said Hi!", obMenuItem.Header));
}

Events will bubble up to the common handler. You can then use the Header text or better the DataContext property to switch and act as needed



回答2:

You could have each menu item execute the same command, thus handling the execution centrally. If you need to distinguish menu items beyond the actual object instance, you can bind the command parameter too:

<MenuItem Header="My Items" ItemsSource="{Binding Path=MyMenuItems}">
    <MenuItem.ItemContainerStyle>
        <Style TargetType="MenuItem">
            <Setter Property="Command" Value="{x:Static local:MyCommands.MyCommand}"/>
            <Setter Property="CommandParameter" Value="{Binding SomeProperty}"/>
        </Style>
    </MenuItem.ItemContainerStyle>
</MenuItem>

SomeProperty is assumed to be a property on each item in your MyMenuItems collection. Your command execution handler would therefore receive the value of SomeProperty for the particular menu item that is clicked.



回答3:

IMHO more general event handler with ability to get item from itemssource

private void DataBoundMenuItem_Click(object sender, RoutedEventArgs e)
{
    // get menu item with ItemsSource bound
    var myItemsMenuItems = sender as MenuItem; 

    // get submenu clicked item constructed from MyMenuItems collection
    var myItemsMenuSubItem = e.OriginalSource as MenuItem; 

    // get underlying MyMenuItems collection item
    var o = myItemsMenuItems
        .ItemContainerGenerator
        .ItemFromContainer(myItemsMenuSubItem);
    // convert to MyMenuItems type ... in our case string
    var itemObj = o as (string);

    // TODO some processing
}

Hope it'l help smbd!



回答4:

If you want a simpler way to access the menu item content:

<MenuItem Header="My Items" ItemsSource="{Binding Path=MyMenuItems}" Click="MenuItem_Click">
    <MenuItem.ItemContainerStyle>
        <Style TargetType="MenuItem">
            <Setter Property="CommandParameter" Value="{Binding}" />
        </Style>
    </MenuItem.ItemContainerStyle>
</MenuItem>

Cod Behind:

private void MenuItem_Click(object sender, RoutedEventArgs e)
{
   var item = ((MenuItem)e.OriginalSource).CommandParameter;   
}