如何使WPF ContextMenu在打开时自动选择第一个项目?

qcbq4gxm  于 2023-03-04  发布在  其他
关注(0)|答案(3)|浏览(159)

当用户点击快捷键(通过将其IsOpen属性更改为true)时,我正在打开一个ContextMenu。但当它打开时,没有选择任何项目。如何使第一个项目被选中,以便用户不必按向下箭头键即可到达它?

ndh0cuux

ndh0cuux1#

试试这个:

<ListBox>
    <ListBoxItem Content="Item">
        <ListBoxItem.ContextMenu>
            <ContextMenu Opened="ContextMenu_Opened">
                <MenuItem Click="some_event" Header="Qwerty"/>
            </ContextMenu>
        </ListBoxItem.ContextMenu>
    </ListBoxItem>
</ListBox>

在代码隐藏中:

private void ContextMenu_Opened(object sender, RoutedEventArgs e)
{
    var contextMenu = sender as ContextMenu;
    (contextMenu.Items[0] as MenuItem).Focus();
}
dgtucam1

dgtucam12#

我不确定您要选择哪个控件(Datagrid、ListView或其他)。但是,您需要检查的第一件事是您的控件是否支持KeyPress事件。如果支持,则尝试附加KeyPress的处理程序,并编写逻辑来检查所需的键并相应地选择项。
您可以查看示例here,它实现了如何基于Enter键选择数据网格单元格/行。

gupuwyp2

gupuwyp23#

这里有一个解决方案,将工作的动态上下文菜单。

    • 1.创建使用自定义菜单项的自定义上下文菜单-而不是默认的MenuItem类。**

要实现这一点,我们需要定制类MyContextMenuMyMenuItem

    • 我的上下文菜单:**
public class MyContextMenu : ContextMenu
{
    private object _currentItem;

    protected override bool IsItemItsOwnContainerOverride(object item)
    {
        bool ret = (item is MyMenuItem) || (item is Separator);
        if (!ret)
        {
            _currentItem = item;
        }

        return ret;
    }

    protected override DependencyObject GetContainerForItemOverride()
    {
        object currentItem = _currentItem;
        _currentItem = null;

        if (UsesItemContainerTemplate)
        {
            DataTemplate itemContainerTemplate = ItemContainerTemplateSelector.SelectTemplate(currentItem, this);
            if (itemContainerTemplate != null)
            {
                object itemContainer = itemContainerTemplate.LoadContent();
                if (itemContainer is MyMenuItem || itemContainer is Separator)
                {
                    return itemContainer as DependencyObject;
                }
                else
                {
                    throw new ImplementationException("InvalidItemContainer!");
                }
            }
        }

        return new MyMenuItem();
    }
}
    • 我的菜单项:**
public class MyMenuItem : MenuItem
{
    public MyMenuItem()
    {

    }
    private object _currentItem;

    protected override bool IsItemItsOwnContainerOverride(object item)
    {
        bool ret = (item is MyMenuItem) || (item is Separator);
        if (!ret)
        {
            _currentItem = item;
        }

        return ret;
    }

    protected override DependencyObject GetContainerForItemOverride()
    {
        object currentItem = _currentItem;
        _currentItem = null;

        if (UsesItemContainerTemplate)
        {
            DataTemplate itemContainerTemplate = ItemContainerTemplateSelector.SelectTemplate(currentItem, this);
            if (itemContainerTemplate != null)
            {
                object itemContainer = itemContainerTemplate.LoadContent();
                if (itemContainer is MyMenuItem || itemContainer is Separator)
                {
                    return itemContainer as DependencyObject;
                }
                else
                {
                    throw new ImplementationException("InvalidItemContainer!");
                }
            }
        }

        return new MyMenuItem();
    }

    public static readonly DependencyProperty IsManuallyFocusedProperty = DependencyProperty.Register(
        nameof(IsManuallyFocused), typeof(bool), typeof(MyMenuItem), new PropertyMetadata(default(bool), IsManuallyFocusedChanged));

    private static void IsManuallyFocusedChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        var item = (MyMenuItem)d;
        if (item.IsManuallyFocused) item.Focus();
    }

    public bool IsManuallyFocused
    {
        get { return (bool)GetValue(IsManuallyFocusedProperty); }
        set { SetValue(IsManuallyFocusedProperty, value); }
    }
}

这两个类都包含两个相同的重写,如here所述。诚然,这有点像黑客,因为它依赖于正确地模仿内部代码。
现在,在创建上下文菜单时,使用新类MyContextMenu
第二个类,MyMenuItem,有一个额外的DependencyProperty,我们稍后将在DataTrigger中使用它。正如您在IsManuallyFocusedChanged中所看到的,如果IsManuallyFocused被设置为true,则会为该项调用Focus()方法。
注意:MyMenuItem.IsManuallyFocused的实际值是不会被读取的,我们只是将该属性用作一种绑定机制,允许我们调用一个方法。

    • 2.调整样式以适应新类型"MyMenuItem"。**

除了我们在这里添加的额外触发器之外,请确保将样式中的所有TargetType规范更改为新的菜单项类型。

<Style x:Key="{x:Type menuControls:MyMenuItem}" TargetType="{x:Type menuControls:MyMenuItem}">
    <Style.Triggers>
        <DataTrigger Binding="{Binding IsManuallyFocused}" Value="True">
            <Setter Property="IsManuallyFocused" Value="True"/>
        </DataTrigger>
    </Style.Triggers>
</Style>

上面的DataTrigger假设菜单项绑定到包含属性IsManuallyFocused including change notifications, either using INotifyPropertyChanged or a DependencyProperty '的类(此处不再进一步描述)。
注意:在本例中,属性具有相同的名称,但也可以使用不同的名称;DataTrigger.Binding必须引用源类上的属性,而Setter.Property必须引用新菜单项类MyMenuItem上的属性。

    • 3.聚焦所需元素。**

假设您可以访问上下文菜单的源项目,选择指定的项目,然后简单地将IsManuallyFocused设置为true
由于DataTrigger,此更改将"转发"到新菜单项类的属性,其中更改处理程序(′ ′)将在绑定菜单项上设置焦点。

    • 评论**

虽然这样做效果不错,但实现一个自定义菜单项和上下文菜单类可能会在以后派上用场,尤其是IsManuallyFocused依赖属性的"误用"会使接缝变得复杂。如果有更好的方法,我希望有另一个更好的答案。

相关问题