Combobox select item only by mouse or Enter key
I have a WPF ComboBox
Now, by pressing up
an开发者_Python百科d down
keys SelectedItem
changes automatically.
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.
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 ComboBoxItem
s 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>
精彩评论