Как открыть раскрывающийся список ComboBox с помощью клавиши со стрелкой вниз

В приложении C# WPF я использую элемент управления ComboBox со свойством IsEditable="True". Когда я нажимаю клавишу со стрелкой вниз в закрытом поле со списком, следующий элемент в поле со списком выбирается без открытия раскрывающегося списка. Как вместо этого открыть раскрывающийся список при нажатии клавиши со стрелкой вниз, аналогично тому, как я могу открыть его с помощью Alt + Стрелка вниз?

Причина, по которой мне нужна возможность открывать раскрывающийся список с помощью клавиши со стрелкой вниз, заключается в том, что я хочу оптимизировать поведение ComboBox только для клавиатуры и доступности средства чтения с экрана, а моя программа чтения с экрана не предоставляет никакой информации о выбранном в данный момент элементе при изменении выбора элемента в ComboBox. с помощью клавиш со стрелками вниз или вверх, не открывая раскрывающийся список, но предоставляет обратную связь только после навигации по открытому раскрывающемуся списку ComboBox при открытии с помощью Alt + стрелка вниз.

На данный момент у меня есть следующий код, который, к сожалению, не работает, поскольку событие PreviewKeyDown не запускается, если я нажимаю только клавишу со стрелкой вниз без модификатора. Он срабатывает для символов, Alt + стрелка вниз и т. д., но не для стрелки вниз.

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
    }

    private void MainWindow_Loaded(object sender, RoutedEventArgs e)
    {
        commandComboBox.PreviewKeyDown += new KeyEventHandler(commandComboBox_PreviewKeyDown);
    }

    private void commandComboBox_PreviewKeyDown(object sender, KeyEventArgs e)
    {
        if (e.Key == Key.Down && !commandComboBox.IsDropDownOpen)
        {
            commandComboBox.IsDropDownOpen = true;
        }
    }

    }

🤔 А знаете ли вы, что...
C# является статически типизированным языком, что обеспечивает высокую безопасность и производительность кода.


1
93
3

Ответы:

Мне удалось это сделать на мероприятии PreviewKeyDown вот так:

private void ComboBox_PreviewKeyDown(object sender, KeyEventArgs e)
{
    if (e.Key == Key.Down)
    {
        ((ComboBox)sender).IsDropDownOpen = true;
    }
}

Этот обработчик событий ожидает нажатия клавиши Down, а затем открывает раскрывающийся список.


Дополню ответ Эфраима Ньюмана. Вы можете изменить поведение элемента, используя «Поведение» или «Прикрепленное свойство». Пример реализации Attached Property:

    public class ComboBoxHelper
    {
        public static bool GetIsDropDownOpenDownArrow(ComboBox cBox)
        {
            return (bool)cBox.GetValue(IsDropDownOpenDownArrowProperty);
        }

        public static void SetIsDropDownOpenDownArrow(ComboBox cBox, bool value)
        {
            cBox.SetValue(IsDropDownOpenDownArrowProperty, value);
        }

        // Using a DependencyProperty as the backing store for IsDropDownOpenDownArrow.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty IsDropDownOpenDownArrowProperty =
            DependencyProperty.RegisterAttached(
                "IsDropDownOpenDownArrow",
                typeof(bool),
                typeof(ComboBoxHelper),
                new PropertyMetadata(false)
                {
                    PropertyChangedCallback = OnIsDropDownOpenDownArrowChanged
                });

        //private static ConditionalWeakTable<ComboBox, delegate> Handlers;
        private static void OnIsDropDownOpenDownArrowChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            ComboBox cBox = (ComboBox)d;
            if (true.Equals(e.NewValue))
            {
                cBox.AddHandler(UIElement.PreviewKeyDownEvent, DownArrowHandler);
            }
            else
            {
                cBox.RemoveHandler(UIElement.PreviewKeyDownEvent, DownArrowHandler);
            }
        }

        private static readonly KeyEventHandler DownArrowHandler = (sender, e) =>
        {
            if (e.Key == Key.Down)
            {
                ComboBox cBox = (ComboBox)sender;
                if (!cBox.IsDropDownOpen)
                {
                    cBox.IsDropDownOpen = true;
                    e.Handled = true;
                }
            }
        };
    }

Пример использования:

    public static class DataExample
    {
        public static ReadOnlyCollection<int> IntCollection { get; } =
            Array.AsReadOnly(Enumerable.Range(0, 10).Select(_ => Random.Shared.Next(100)).ToArray());
    }
    <UniformGrid Rows = "1">
        <ComboBox ItemsSource = "{x:Static local:DataExample.IntCollection}"
                  SelectedIndex = "0" VerticalAlignment = "Center" HorizontalAlignment = "Center"/>
        <ComboBox ItemsSource = "{x:Static local:DataExample.IntCollection}"
                  SelectedIndex = "0" VerticalAlignment = "Center" HorizontalAlignment = "Center"
                  local:ComboBoxHelper.IsDropDownOpenDownArrow = "True"/>
    </UniformGrid>

Решено

Добавьте в проект новый объект класса (SHIFT+F2), назовите его ComboBoxEx (или как вы предпочитаете), затем замените определение класса этим кодом, сохраняя, конечно, namespace. Перестройте решение.

using System.ComponentModel;
using System.Windows.Controls;
using System.Windows.Input;

[ToolboxItem(true)]
public class ComboBoxEx : ComboBox
{
    public ComboBoxEx() { }

    protected override void OnPreviewKeyDown(KeyEventArgs e) {
        int index = SelectedIndex;
        base.OnPreviewKeyDown(e);
        if (e.Key == Key.Down && !IsDropDownOpen) {
            IsDropDownOpen = true;
            SelectedIndex = index;
        }
    }
}

Если у вас уже есть поле со списком в XAML, замените имя экземпляра на local:ComboBoxEx (или любое другое имя, которое вы решили присвоить классу) и запустите проект.

SelectedIndex сохраняется перед вызовом base.OnPreviewKeyDown(e), поскольку поведение по умолчанию перемещает выбор к следующему элементу в списке. Это гарантирует, что при открытии раскрывающегося списка выбранный элемент отображается как текущий текст. Измените по мере необходимости.