开发者

Combobox select item only by mouse or Enter key

开发者 https://www.devze.com 2023-04-12 00:24 出处:网络
I have a WPF ComboBox I need to change default behaivor in popup list. Now, by pressing up an开发者_Python百科d down keys SelectedItem changes automatically.

I have a WPF ComboBox

I need to change default behaivor in popup list.

Now, by pressing up an开发者_Python百科d down keys SelectedItem changes automatically.

I need change SelectedItem only by pressing Enter key, or clicking by mouse.

How can it be done?

I've subclassed 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);
}

This code doesn't work - no popup is shown and user can't select items. What I shoud write and where? :)

Thanks.

UPD1:

I need same functionality as ComboBox's popup is open and user can select items by mouse.

Each item can be hovered by mouse, but not selected. Selection become only by pressing mouse button. I need the same. 'Up' and 'Down' only highlights items in popup, but SelectedItem will be changed only by pressing Enter or mouse clicking.

UPD2: If I press by mouse on button, that opens Popup in ComboCox, I can highlight items in Popup by mouse, but SelectedItem will change only if I click on item.

I need same functionality by keyboard. If I start typing somewhat in ComboBox, Popup opens. And I have to hightlight items by keyboard Up and Down. TextBox in ComboBox must not change during highlighting and SelectedItem must change only if I press Enter (or mouse click)

UPD3: Link to demo solution: download


You should have this event handled on all the ComboBoxItems in a combobox.

   <ComboBox.Resources>
        <Style TargetType="{x:Type ComboBoxItem}">
           <EventSetter Event="PreviewKeyDown" Handler="OnPreviewKeyDown" />
        </Style> 
  </ComboBox.Resources>

EDIT:

In code behind, you can add folowing code in MyComboBox's constructor after InitializeComponent() do this...

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

Hope this helps.


The code you have seems to work fine, just add a check to see if the DropDown is open before cancelling the Key Event

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);
}


In my opinion, you should create property - IsKeyNavigation (like 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);
}

And if Key.Up or Key.Down is pressed you should Define navigated element and change the VisualState.


Here is the solution which worked for me -

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);
    }
}


Better use the ComboBox.OnDropDownClosed(EventArgs) Method

https://learn.microsoft.com/de-de/dotnet/api/system.windows.forms.combobox.ondropdownclosed?view=netframework-4.7.2#System_Windows_Forms_ComboBox_OnDropDownClosed_System_EventArgs_

PS: For Infracistics Controls (UltraComboEditor) it´s the OnAfterCloseUp Event.


Install a nuget package System.Windows.Interactivity.WPF, create a class like following (NB: left/right arrows also change selection in a closed focused 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;
    }
}

Add this behavior class to the xaml:

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

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号