3

在这个应用程序中,我在用户控件中放置了一个菜单。

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

我想将菜单上的所有“单击”事件路由并冒泡到用户控件中的单个单击事件。所以我实现了:

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

4 回答 4

3

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

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

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

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

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

于 2013-11-01T12:13:59.897 回答
2

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

你会如何解决这个问题?

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

    <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 事件时,您期望的是“Click”,而不是单击控件内的菜单项。

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

于 2013-11-01T10:56:05.903 回答
2

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

public partial class YourUserControl : UserControl
{
    ...

    public event RoutedEventHandler Click
    {
        add { AddHandler(MenuItem.ClickEvent, value); }
        remove { RemoveHandler(MenuItem.ClickEvent, value); }
    }
}
于 2013-11-01T11:17:19.573 回答
2

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

<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
    }
}
于 2013-11-01T12:38:12.350 回答