Focus management Tips & Tricks for nested ListView's part 1
Hi, as every WPF developer would agree with me that handling focus in WPF is a nightmare and every now and then you come across scenarios which makes you pull your hair I recently came across one(more) such scenario and it was a tough task to make focus work properly, I think it will be useful to blog about my experience and solutions I have implemented, so here is the first and easiest part of this series.
Scenario: I had a control which consists of nested ListView’s i.e. a parent ListView which can have other ListView’s as its children and each child ListView will have multiple child controls. Apart from this each ListViewItem implements Excel cell like behavior(using ExcelCellBehavior), so whenever a row in ListView gets focus its internal editable control(like TextBox) automatically gets focus.
Problem 1: Moving around ListView using arrow keys(Up/Down); this was not easy to accomplish as the focus used to be present with the editable control present in the selected ListViewItem . So once a ListViewItem gets selected it was not possible to move around using arrow keys.
Solution: This was easiest problem to solve and obvious solution was to handle the PreviewKeyDown event and change the SelectedIndex of ListView -
<ListView.ItemContainerStyle>
<Style TargetType="{x:Type ListViewItem}">
<EventSetter
Event="PreviewKeyDown"
Handler="HandleListViewItemPreviewKeyDown" />
</Style>
</ListView.ItemContainerStyle>
private void HandleListViewItemPreviewKeyDown(object sender, KeyEventArgs e)
{
// No need to do anything if its already handled/Del or Enter key is not pressed/ its a repeated key down event
if (e.Handled)
{
return;
}
bool isNavigationKey = e.Key == Key.Down || e.Key == Key.Up;
// No need to do anything if its a repeated key down event and pressed key is not Up || Down arrow
if (e.IsRepeat && !isNavigationKey)
{
return;
}
switch (e.Key)
{
case Key.Down:
case Key.Up: // If Shift + Return is pressed then move focus upwards
if (e.Key == Key.Up || (e.Key == Key.Return && e.KeyboardDevice.Modifiers == ModifierKeys.Shift))
{
// Move the focus to previous row if Enter/Return key is pressed
if (this.SelectedIndex > 0)
{
this.SelectedIndex = this.SelectedIndex - 1;
}
}
else
{
// Move the focus to next row if Enter/Return or Down key is pressed
if (this.SelectedIndex >= 0 && this.Items.Count > this.SelectedIndex + 1)
{
this.SelectedIndex = this.SelectedIndex + 1;
}
}
break;
default:
break;
}
}
One more thing we need to do is to handle the Up and Down arrow keys for the editable controls(in ExcelCellBehavior)
// No need to start editing if any modifier key(ctrl, Alt, Shift) Or
// navigation key(Up/Down) is pressed Or it is a repeated key down event notification
if (e.KeyboardDevice.Modifiers != ModifierKeys.None || e.Key == Key.Down || e.Key == Key.Up || e.IsRepeat)
{
return;
}