如何从MenuItems冒泡事件

时间:2013-11-01 10:37:31

标签: c# wpf .net-4.5

在这个应用程序中,我将一个Menu放在Usercontrol中。

<UserControl>
    <Grid>
        <Menu x:Name="mainMenu">
            <MenuItem  Header="File">
                <MenuItem Header="Open Analysis">
                    <MenuItem Header="Load all" />
                    <MenuItem Header="Select from tracks" />
                </MenuItem>
            </MenuItem>
        </Menu>
     </Grid>
</UserControl>

我想将菜单上的所有“点击”事件路由并冒泡到Usercontrol中的单个Click事件。 所以我实施了:

public static readonly RoutedEvent ClickEvent = eventManager.RegisterRoutedEvent("Click", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(MenuItem));
public event RoutedEventHandler Click
{
    add { AddHandler(ClickEvent, value); }
    remove { RemoveHandler(ClickEvent, value); }
}

但是在例外情况下失败了:

{"RoutedEvent Name 'Click' for OwnerType 'System.Windows.Controls.MenuItem' 
already used."}

我想这是因为有多个MenuItems ,因此事件是多次注册,所以它第二次想要注册就失败了。

但是,如何以一种良好的主要方式将所有这些事件“捆绑”(如奇异处理程序附加点)到用户控件中的单个事件中?由于某些子菜单由数据库提供,因此是动态建立的。

4 个答案:

答案 0 :(得分:3)

除了已经说过的内容之外,我还会研究一个简单的消息传递解决方案。

每次单击任何菜单项时,都可以广播MenuItemClickedMessage类型的消息。

public class MenuItemClickedMessage
{
   public MenuItem MenuItemClicked { get; set; }
}

然后,您可以拥有一个侦听该事件的组件。收到事件后,您可以从MenuItemClicked属性中提取菜单项并做出相应的反应。

这需要对简单的消息传递解决方案进行一些研究:http://rachel53461.wordpress.com/2011/05/28/switching-between-viewsusercontrols-using-mvvm/

答案 1 :(得分:2)

与直觉相反,您收到的错误不是由于多个菜单项,而是由于Click事件已经存在。事件注册是静态的,WPF框架已经使用它。

你会如何解决这个问题?

您可以为菜单创建样式;样式可以在WPF中包含EventSetters。这允许您为容器中的任何菜单项注册单击处理程序:

    <Grid>
        <Grid.Resources>
            <Style TargetType="MenuItem">
                <EventSetter Event="Click" Handler="OnMenuItemClick" />
            </Style>
        </Grid.Resources>
        <Menu x:Name="mainMenu">
           ...
        </Menu>
    </Grid>

然后在您的处理程序中确保将Handled属性设置为true,以停止冒泡:

 private void OnMenuItemClick(object sender, RoutedEventArgs e)
 {
     e.Handled = true;
 }

<强>更新 您应该考虑不在UserControl上触发“Click”事件,因为这可能是一个设计问题:当有人使用您的UserControl并且在用户控件上触发Click事件时,您需要“点击”,而不是点击在控件内的菜单项上。

考虑使用自定义事件名称,例如MenuItemClick,您可以在事件参数中提供菜单项的标识符。

答案 2 :(得分:2)

虽然我强烈建议不要这样做,但是如Bas所指出的那样声明一个独特的新事件,你可以简单地重用MenuItem类中现有的RoutedEvent:

public partial class YourUserControl : UserControl
{
    ...

    public event RoutedEventHandler Click
    {
        add { AddHandler(MenuItem.ClickEvent, value); }
        remove { RemoveHandler(MenuItem.ClickEvent, value); }
    }
}

答案 3 :(得分:2)

MenuItemClick - 是一个路由事件,并使用RoutedEventArgs.OriginalSource,而不是发件人。这指向最初触发事件的控件。

<Grid>
        <Menu x:Name="mainMenu">
            <MenuItem  Header="File" Click="OnFileMenuItemClicked">
                <MenuItem Header="Open Analysis">
                    <MenuItem Header="Load all" />
                    <MenuItem Header="Select from tracks" />
                </MenuItem>
            </MenuItem>
        </Menu>
</Grid>


private void OnFileMenuItemClicked(object sender, RoutedEventArgs e)
{
    MenuItem item = e.OriginalSource as MenuItem;
    if(null != item)
    {
        // Handle the menuItem click here
    }
}