ListBox中的奇怪选项卡行为

时间:2015-01-15 09:31:53

标签: c# wpf listbox

我有一个简单的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"禁用虚拟化,则Tab键按预期工作,但我不能禁用它,因为某些列表非常大。

更新 我正在尝试手动处理它,它仍然以相同的方式工作:

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

此外,我试图在ListBox中找到ScrollViewer并滚动到顶部,然后聚焦第一个项目,这是不可靠的(看起来像滚动是异步发生的,因为有时它会标记到列表的中间)。

2 个答案:

答案 0 :(得分: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();
    }
}

答案 1 :(得分:0)

KeyboardNavigation.TabNavigation =“Cycle”