如何为wpf ListBox自定义ContextMenu

时间:2017-10-14 23:14:59

标签: wpf listbox contextmenu

考虑一个在运行时可能没有完全用ListBoxItem填充的ListBox。我想为该ListBox自定义ContextMenu,具体取决于用户是右键单击其中一个ListBoxItem还是没有项目的空白区域。

我遇到的问题是,在后一种情况下,没有触发ListBox事件,只触发ContextMenuOpening事件。从那个事件我无法弄清楚如何确定用户是否右键单击现有的ListBoxItem。

我查看了所有ListBox属性和事件,但是canot提出了一种区分这两种情况的方法。我考虑使用样式触发器,但同样,核心问题是在空白处右键单击不会触发任何ListBox事件。我还审查了SO建议的链接,但没有人谈到这个问题。

如何做到这一点?

3 个答案:

答案 0 :(得分:1)

这是一种更简洁的方法,可以为ContextMenu及其ListBox定义两个不同的ListBoxItem,而无需任何代码隐藏检查,它就像一个魅力:

<ListBox ContextMenu="{StaticResource ListContextMenu}">
    <ListBox.Resources>
        <!-- Context Menu when right click on selected List Item -->
        <ContextMenu x:Key="ItemContextMenu">
            <MenuItem Header="Eat"></MenuItem>
            <MenuItem Header="Delete"></MenuItem>
            <Separator></Separator>
            <MenuItem Header="Send To Friend"></MenuItem>
        </ContextMenu>
        <!-- Context Menu when right click on listbox space -->
        <ContextMenu x:Key="ListContextMenu">
            <MenuItem Header="Save Fruits"></MenuItem>
            <MenuItem Header="Add Or Remove Fruits"></MenuItem>
        </ContextMenu>
        <!-- Applying Context Menu to ListBoxItem with Style -->
        <Style TargetType="{x:Type ListBoxItem}">
            <Setter Property="ContextMenu" Value="{StaticResource ItemContextMenu}">
            </Setter>
        </Style>
    </ListBox.Resources>

    <ListBox.ContextMenu>
        <Binding Source="{StaticResource ListContextMenu}"></Binding>
    </ListBox.ContextMenu>


    <ListBoxItem>Banana</ListBoxItem>
    <ListBoxItem>Apple</ListBoxItem>
    <ListBoxItem>Orange</ListBoxItem>
</ListBox>

如果要根据所选项目动态修改ListBoxItem.ContextMenu的内容,可以挂钩ContextMenu.Opened事件的处理程序,并在该处理程序中检查所选项目并添加新{{1}在代码中收集到MenuItem

请注意,这两个ContextMenu只会在ContextMenu内显示,因为它们是在ListBox中定义的。

答案 1 :(得分:0)

我设计了一个不漂亮但有效的解决方案。基本上,每当ListBox失去焦点时,我强制它没有通过设置

选择任何内容
ListBox.SelectedIndex = -1

现在,在ContextMenuOpening事件中,当焦点返回到ListBox时,可以获取ListBox.SelectedIndex,如果它是-1,则用户单击ListBox的空白部分,否则ListBoxItem是点击了。

编码这个逻辑非常简单。

答案 2 :(得分:0)

也许我没有达到您的意图:您可以在ContextMenuXAML中定义不同的Code-behind,如下所示:

XAML:

<ListBox PreviewMouseDown="ListBox_PreviewMouseDown">
    <ListBox.ItemContainerStyle>
        <Style TargetType="{x:Type ListBoxItem}">
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="{x:Type ListBoxItem}">
                        <ContentPresenter>
                            <ContentPresenter.ContextMenu>
                                <ContextMenu>
                                    <MenuItem Header="ListBoxItem"></MenuItem>
                                    <MenuItem Header="ListBoxItem"></MenuItem>
                                    <MenuItem Header="ListBoxItem"></MenuItem>
                                </ContextMenu>
                            </ContentPresenter.ContextMenu>
                        </ContentPresenter>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>
    </ListBox.ItemContainerStyle>
    <ListBoxItem>Item1</ListBoxItem>
    <ListBoxItem>Item2</ListBoxItem>
    <ListBoxItem>Item2</ListBoxItem>
    <ListBoxItem>Item2</ListBoxItem>
    <ListBox.ContextMenu>
        <ContextMenu>
            <MenuItem Header="ListBox"></MenuItem>
            <MenuItem Header="ListBox"></MenuItem>
            <MenuItem Header="ListBox"></MenuItem>
        </ContextMenu>
    </ListBox.ContextMenu>
</ListBox>

代码隐藏:

private void ListBox_PreviewMouseDown(object sender, MouseButtonEventArgs e)
{
    if(e.ChangedButton == MouseButton.Right)
    {
        var listBoxItem = e.Source as ListBoxItem;
        if (listBoxItem != null)
        {
            // clicked on ListBoxItem, customize the ContextMenu
        }

        var listBox = e.Source as ListBox;
        if (listBox != null)
        {
            // clicked on ListBox, customize the ContextMenu
        }
    }
}