仅通过鼠标或按下Enter键选择Combobox项目

6
我有一个WPF的ComboBox组件,需要更改弹出列表的默认行为。
现在,通过按上下箭头键,SelectedItem会自动更改。我需要只有通过按Enter键或点击鼠标才能更改SelectedItem。
如何实现?
我已经对ComboBox进行了子类化:
protected override void OnPreviewKeyDown(System.Windows.Input.KeyEventArgs e)
{
    Debug.Write("Pressed " + e.Key+ " ");
    if (e.Key == System.Windows.Input.Key.Up || e.Key == System.Windows.Input.Key.Down)
    {
        // ???
        e.Handled = true;
        return;
    }
    base.OnPreviewKeyDown(e);
}

这段代码不起作用——没有弹出窗口,用户也无法选择项目。我应该在哪里写什么?
谢谢。
UPD1: 我需要与 ComboBox 的弹出窗口相同的功能,用户可以通过鼠标选择项目。每个项目都可以被鼠标悬停,但不能被选中。只有按下鼠标按钮才能进行选择。我需要相同的功能。"上"和"下"只会在弹出窗口中突出显示项目,但是只有通过按"Enter"或鼠标单击才会更改 SelectedItem。
UPD2: 如果我用鼠标点击打开 ComboCox 中的 Popup 按钮,我可以用鼠标在 Popup 中突出显示项目,但只有当我点击项目时,SelectedItem 才会更改。
我需要相同的键盘功能。如果我在 ComboBox 中开始输入,Popup 就会打开。我必须使用键盘 "Up" 和 "Down" 来突出显示项目。ComboBox 中的 TextBox 在突出显示时不得更改,并且只有在我按下 "Enter"(或鼠标单击)时,SelectedItem 才会更改。
UPD3: 演示解决方案链接:下载
6个回答

3
你应该在下拉框中的所有ComboBoxItem上处理此事件。
   <ComboBox.Resources>
        <Style TargetType="{x:Type ComboBoxItem}">
           <EventSetter Event="PreviewKeyDown" Handler="OnPreviewKeyDown" />
        </Style> 
  </ComboBox.Resources>

编辑:

在代码后台,您可以在MyComboBox的构造函数中添加以下代码,在InitializeComponent()之后执行此操作...

  var comboBoxItemstyle = new Style(typeof (ComboBoxItem));  
  comboBoxItemstyle.Setters.Add(
        new EventSetter(PreviewKeyDownEvent,
                new KeyEventHandler(OnPreviewKeyDown)));
  this.Resources.Add(typeof (ComboBoxItem), comboBoxItemstyle);

希望这有所帮助。

我必须从子类化的 MyComboBox 类中完成它,而不是在 XAML 中。这是否可能? - Lari13
我提供了XAML,你的处理程序可以在xaml.cs文件中,或者如果你正在使用MVVm,则可以通过附加行为来完成。 - WPF-it
我没有XAML。我只有一个名为MyComboBox : ComboBox的类,因此我不需要附加行为,因为我已经在ComboBox内部了 :) - Lari13
但是你可以通过在MyComboBox.Resources中编程方式添加“ComboboxItem”样式,使用“this.resources.Add()”,不是吗? - WPF-it
请问您能提供一个代码示例吗?我不明白它怎么做。您是否建议在XAML代码中编写带有代码后台的资源,并将其添加到“MyComboBox”中?我认为这不是一个好主意。我相信,还有其他解决方案。 - Lari13

3
您的代码似乎运行良好,只需添加一个检查,以查看在取消键事件之前下拉菜单是否已打开。
protected override void OnPreviewKeyDown(KeyEventArgs e)
{
    Debug.Write("Pressed " + e.Key + " ");
    if (!base.IsDropDownOpen && (e.Key == Key.Up || e.Key == Key.Down))
    {
        e.Handled = true;
        return;
    }
    base.OnPreviewKeyDown(e);
}

@Lari 请看这个问题:http://stackoverflow.com/q/7145876/302677 建议更改ComboBox的选择项行为,使得只有在关闭ComboBox的DropDown时才更改SelectedItem。如果您想在关闭DropDown并按箭头键时保持选择行为,则还可以处理键事件以更新SelectedItem - Rachel

1

这是对我有效的解决方案 -

public class CustomComboBox : ComboBox
{
    private int _currentItemIndex;
    private string _rowColor = "#E7E7E7";
    private string _selectedRowColor = "#FFFFC6";        

    protected override void OnDropDownOpened(EventArgs e)
    {
        _currentItemIndex = base.SelectedIndex;
        base.OnDropDownOpened(e);
    }

    protected override void OnPreviewKeyDown(KeyEventArgs e)
    {
        if (base.IsDropDownOpen)
        {
            if (e.Key == Key.Up || e.Key == Key.Down)
            {
                ComboBoxItem currentItem;
                var colorConverter = new BrushConverter();

                if (_currentItemIndex > -1 && _currentItemIndex != base.SelectedIndex)
                {
                    currentItem = (ComboBoxItem)base.ItemContainerGenerator.ContainerFromIndex(_currentItemIndex);
                    currentItem.Background = (Brush)colorConverter.ConvertFromString(_rowColor);
                }

                if (e.Key == Key.Up)
                {
                    _currentItemIndex -= 1;
                    if (_currentItemIndex < 0)
                    {
                        _currentItemIndex = 0;
                    }
                }
                else if (e.Key == Key.Down)
                {
                    _currentItemIndex += 1;
                    if (_currentItemIndex > base.Items.Count - 1)
                    {
                        _currentItemIndex = base.Items.Count - 1;
                    }
                }

                currentItem = (ComboBoxItem)base.ItemContainerGenerator.ContainerFromIndex(_currentItemIndex);
                currentItem.Background = (Brush)colorConverter.ConvertFromString(_selectedRowColor);
                currentItem.BringIntoView();

                e.Handled = true;
                return;
            }
            else if (e.Key == Key.Enter)
            {
                base.SelectedItem = base.Items[_currentItemIndex];
            }
        }
        base.OnPreviewKeyDown(e);
    }

    protected override void OnDropDownClosed(EventArgs e)
    {
        if (_currentItemIndex > -1 && base.Items[_currentItemIndex] != base.SelectedItem)
        {
            var colorConverter = new BrushConverter();
            ComboBoxItem currentItem = (ComboBoxItem)base.ItemContainerGenerator.ContainerFromIndex(_currentItemIndex);
            currentItem.Background = (Brush)colorConverter.ConvertFromString(_rowColor);
        }
        base.OnDropDownClosed(e);
    }
}

0

我认为,你应该创建一个属性 - IsKeyNavigation(就像IsMouseOver)

protected override void OnPreviewKeyDown(System.Windows.Input.KeyEventArgs e)
{
    Debug.Write("Pressed " + e.Key+ " ");
    if (e.Key == System.Windows.Input.Key.Up || e.Key == System.Windows.Input.Key.Down)
    {
        VisualStateManager.GoToState(this, "KeyNavigation", true);
        e.Handled = true;
        return;
    }
    base.OnPreviewKeyDown(e);
}

如果按下Key.Up或Key.Down,则应定义导航元素并更改VisualState。


0

0
安装一个NuGet包System.Windows.Interactivity.WPF,创建一个类似以下的类(注意:左/右箭头也会在关闭的聚焦ComboBox中更改选择):
public class ComboBoxCustomBehaviour: Behavior<ComboBox>
{
    private readonly ISet<Key> _dropdownBlockedKeys = new HashSet<Key>{Key.Up, Key.Down, Key.Left, Key.Right};

    protected override void OnAttached()
    {
        AssociatedObject.PreviewKeyDown += AssociatedObject_KeyDown;
    }

    protected override void OnDetaching()
    {
        AssociatedObject.PreviewKeyDown -= AssociatedObject_KeyDown;
    }

    private void AssociatedObject_KeyDown(object sender, KeyEventArgs e)
    {
        if (_dropdownBlockedKeys.Contains(e.Key))
            e.Handled = true;
        // Use following line, when you need to stop selection only on closed ComboBox
        // e.Handled = !((ComboBox)sender).IsDropDownOpen;
    }
}

将此行为类添加到XAML中:

<ComboBox>
    <ComboBoxItem>Item 1</ComboBoxItem>
    <ComboBoxItem>Item 2</ComboBoxItem>
    <ComboBoxItem>Item 3</ComboBoxItem>
    <i:Interaction.Behaviors>
        <local:ComboBoxCustomBehaviour />
    </i:Interaction.Behaviors>
</ComboBox>

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接