Странное поведение вкладки в ListBox

1

У меня есть простой ListBox:

<Style TargetType="ListBoxItem">
        <Setter Property="IsTabStop" Value="False" />
</Style>
<ListBox ItemsSource="{Binding Items}" HorizontalAlignment="Stretch" KeyboardNavigation.TabNavigation="Local">
        <ListBox.ItemTemplate>
            <DataTemplate>
                <StackPanel Orientation="Horizontal">
                    <RadioButton />
                    <TextBlock Text="{Binding Name}" />
                </StackPanel>
            </DataTemplate>
        </ListBox.ItemTemplate>
    </ListBox>

И когда я перебираю его, у него странное (или нежелательное) поведение. У меня есть 100 предметов, и все они не подходят на экране, поэтому есть ScrollViewer и VirtualizingStackPanel, а tabbing работает нормально, пока не дойдет до конца списка, а затем он переместит 20 позиций назад, в следующий раз, когда он скачет 21 позицию назад, в следующий раз 22 позиции назад.

Есть ли способ заставить его перейти к первому элементу списка, как только он достигнет конца? Я пробовал все возможные значения KeyboardNavigation.TabNavigation, не помог. Shift-Tab работает так же, как с первого пункта перехода до 20-го, в следующий раз 21-го и т.д.

Если я отключу виртуализацию с помощью VirtualizingStackPanel.IsVirtualizing="False", то табуляция работает так, как ожидалось, но я не могу запретить ее отключать, потому что некоторый список довольно велик.

Обновление: я пытаюсь обрабатывать его вручную, и он работает по-разному:

private void ListBox_OnPreviewKeyDown(object sender, KeyEventArgs e)
    {
        if (e.Key != Key.Tab)
        {
            return;
        }

        var focusedItem = FindParent<ListBoxItem>(Keyboard.FocusedElement as DependencyObject);

        if (focusedItem != null && focusedItem.Content == ListBox.Items[ListBox.Items.Count - 1])
        {
            ListBox.MoveFocus(new TraversalRequest(FocusNavigationDirection.First));
            e.Handled = true;               
        }
    }

Также я попытался найти ScrollViewer в ListBox и прокрутить вверх, а затем сфокусировать первый элемент, который работает неуправляемо (похоже, что прокрутка происходит асинхронно, так как иногда она отображается до середины списка).

  • 0
    Как насчет подписки на KeyDown и если нажата клавиша TAB и выбран последний элемент, вы выбираете первый?
  • 0
    Это может быть решением, но у меня все еще есть надежда, что WPF сам по себе может решить эту проблему :)
Показать ещё 6 комментариев
Теги:
wpf
listbox

2 ответа

0
Лучший ответ

У меня наконец есть рабочее решение, оно не так элегантно, как мне бы хотелось, но похоже, что это работает.

Если у кого-то есть лучшее/меньшее рабочее решение, отправьте ответ.

public class FixVirtualizedTabbingBehavior : Behavior<ListBox>
{
    protected override void OnAttached()
    {
        AssociatedObject.PreviewKeyDown += AssociatedObjectOnPreviewKeyDown;
        AssociatedObject.GotKeyboardFocus += AssociatedObjectGotKeyboardFocus;
        base.OnAttached();
    }

    void AssociatedObjectGotKeyboardFocus(object sender, KeyboardFocusChangedEventArgs e)
    {
        var listBox = ((ListBox)sender);

        if (e.OldFocus != null && ((DependencyObject)e.OldFocus).FindParent<ListBox>() != listBox)
        {
            var direction = Keyboard.Modifiers.HasFlag(ModifierKeys.Shift)
                ? FocusNavigationDirection.Last
                : FocusNavigationDirection.First;
            MoveFocus(listBox, direction);
        }
    }

    private void AssociatedObjectOnPreviewKeyDown(object sender, KeyEventArgs keyEventArgs)
    {
        if (keyEventArgs.Key != Key.Tab)
        {
            return;
        }

        var listBox = ((ListBox)sender);
        int index;
        FocusNavigationDirection direction;

        if (Keyboard.Modifiers.HasFlag(ModifierKeys.Shift))
        {
            index = 0;
            direction = FocusNavigationDirection.Previous;
        }
        else
        {
            index = listBox.Items.Count - 1;
            direction = FocusNavigationDirection.Previous;
        }

        var focusedItem = ((DependencyObject)Keyboard.FocusedElement).FindParent<ListBoxItem>();

        if (focusedItem == null || focusedItem.Content != listBox.Items[index])
        {
            return;
        }

        keyEventArgs.Handled = true;

        MoveFocus(listBox, direction);
    }

    private void MoveFocus(ListBox listBox, FocusNavigationDirection direction)
    {
        var scrollViewer = VisualTreeExtensions.FindVisualChildren<ScrollViewer>(listBox).First();

        if (direction == FocusNavigationDirection.First)
        {
            scrollViewer.ScrollToTop();
        }
        else
        {
            scrollViewer.ScrollToBottom();
        }

        Dispatcher.Invoke(new Action(() => { listBox.MoveFocus(new TraversalRequest(direction)); }),
            DispatcherPriority.ContextIdle, null);
    }

    protected override void OnDetaching()
    {
        AssociatedObject.PreviewKeyDown -= AssociatedObjectOnPreviewKeyDown;
        AssociatedObject.GotKeyboardFocus -= AssociatedObjectGotKeyboardFocus;          

        base.OnDetaching();
    }
}
0

KeyboardNavigation.TabNavigation = "Цикл"

  • 0
    Не могли бы вы реализовать свой ответ во фрагменте кода спрашивающего? Это пойдет на пользу как любителю, так и будущим посетителям сайта.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню