0

I have a virtualized ItemsControl with a list of either labels or textboxes. Virtualisation is needed because of the amount of data. Now i want tab through the liste with the keyboard. This works great until it reaches the end of the visible list. Than the focus leaves the list. Is there any way to "scroll" the list for the next focusable control? The problem is that not every item in the list has a focusable control.

Here is an example:

Here is an example:

Is there any working solution? For example to load 10 more items as visible. Or find the last loaded focusable item and scroll by code. Or after showing the list laod all data in the background. RAM is not the bottleneck it is the rendertime of the list.

I have followed these Virtualizing an ItemsControl?

Here is the not working example

<ItemsControl  DockPanel.Dock="Top" x:Name="lb" Height="200" ItemsSource="{Binding testList}" 
             KeyboardNavigation.TabNavigation="Cycle"
             VirtualizingStackPanel.IsVirtualizing="True" 
            VirtualizingStackPanel.VirtualizationMode="Standard" 
              ScrollViewer.CanContentScroll="True" 
                  AlternationCount="4"
             >

        <ItemsControl.ItemsPanel>
            <ItemsPanelTemplate>
                <VirtualizingStackPanel IsItemsHost="True" Orientation="Vertical" x:Name="virtualizingStackPanel" />
            </ItemsPanelTemplate>
        </ItemsControl.ItemsPanel>
        <ItemsControl.ItemTemplate>
            <DataTemplate>
                <StackPanel>
                    <TextBox Text="{Binding Path=., Mode=OneWay}" Name="txtTest">
                        <TextBox.Style>
                            <Style TargetType="TextBox">
                                <Setter Property="Visibility" Value="Collapsed" />
                            </Style>
                        </TextBox.Style>
                    </TextBox>
                    <Label >space</Label>
                </StackPanel>
                <DataTemplate.Triggers>
                    <Trigger Property="ItemsControl.AlternationIndex" Value="0">
                        <Setter Property="Visibility" Value="visible" TargetName="txtTest"/>
                    </Trigger>
                </DataTemplate.Triggers>
            </DataTemplate>
        </ItemsControl.ItemTemplate>
        <ItemsControl.Template>
            <ControlTemplate>
                <Border
        BorderThickness="{TemplateBinding Border.BorderThickness}"
        Padding="{TemplateBinding Control.Padding}"
        BorderBrush="{TemplateBinding Border.BorderBrush}"
        Background="{TemplateBinding Panel.Background}"
        SnapsToDevicePixels="True">
                    <ScrollViewer
                Padding="{TemplateBinding Control.Padding}"
                Focusable="False" >
                        <ItemsPresenter
                    SnapsToDevicePixels="{TemplateBinding UIElement.SnapsToDevicePixels}" Name="presenter"/>
                    </ScrollViewer>
                </Border>
            </ControlTemplate>
        </ItemsControl.Template>
    </ItemsControl>

Code Behind

 public List<string> testList {

        get
        {

            List< string> a = new List<string>();
            for (int i = 0; i < 10000; i++)
            {
                a.Add(i.ToString());
            }
            return a;
        }


    }
Alex
  • 97
  • 9

1 Answers1

1

Set the KeyboardNavigation.TabNavigation property to Cycle and the IsTabStop property of the container to false. This works for me:

<ListBox x:Name="lb" Height="400" KeyboardNavigation.TabNavigation="Cycle">
    <ListBox.ItemContainerStyle>
        <Style TargetType="ListBoxItem">
            <Setter Property="IsTabStop" Value="False"/>
        </Style>
    </ListBox.ItemContainerStyle>
    <ListBox.ItemTemplate>
        <DataTemplate>
            <TextBox Text="{Binding Path=., Mode=OneWay}" />
        </DataTemplate>
    </ListBox.ItemTemplate>
</ListBox>

WPF: How To Tab Between Items In a ListBox: https://social.technet.microsoft.com/wiki/contents/articles/25152.wpf-how-to-tab-between-items-in-a-listbox.aspx

Your example works. but in my projekt not evey item has a focusable control.

Then you will have to write some code. You could for example handle the PreviewKeyDown event for the container:

<ItemsControl.ItemContainerStyle>
    <Style TargetType="ContentPresenter">
        <EventSetter Event="PreviewKeyDown" Handler="lb_PreviewKeyDown" />
    </Style>
</ItemsControl.ItemContainerStyle>

Here is some sample code that should give you the idea:

private void lb_PreviewKeyDown(object sender, KeyEventArgs e)
{
    if (e.Key == Key.Tab)
    {
        Dispatcher.BeginInvoke(new Action(() =>
        {
            ContentPresenter cp = sender as ContentPresenter;
            VirtualizingStackPanel sp = FindParent<VirtualizingStackPanel>(cp);
            if (sp != null)
            {
                int uiIndex = sp.Children.IndexOf(cp);
                if (uiIndex > -1)
                {
                    ContentPresenter cpp;
                    while (++uiIndex < sp.Children.Count - 1)
                    {
                        cpp = sp.Children[uiIndex] as ContentPresenter;
                        if (cpp != null)
                        {
                            TextBox textBox = FindChild<TextBox>(cpp);
                            if (textBox != null && textBox.Visibility == Visibility.Visible)
                                return;
                        }
                    }

                    //no TextBox was found. generate the containers programmatically
                    int sourceIndex = lb.Items.IndexOf(cp.DataContext);
                    if (sourceIndex > -1)
                    {
                        ScrollViewer sv = FindChild<ScrollViewer>(lb);
                        if (sv != null)
                        {
                            while (++sourceIndex < lb.Items.Count - 1)
                            {
                                cpp = lb.ItemContainerGenerator.ContainerFromIndex(sourceIndex) as ContentPresenter;
                                while (cpp == null)
                                {
                                    sv.ScrollToVerticalOffset(sv.VerticalOffset + 1);
                                    lb.UpdateLayout();
                                    cpp = lb.ItemContainerGenerator.ContainerFromIndex(sourceIndex) as ContentPresenter;
                                }

                                TextBox textBox = FindChild<TextBox>(cpp);
                                if (textBox != null && textBox.Visibility == Visibility.Visible)
                                {
                                    textBox.Focus();
                                    return;
                                }
                            }
                        }
                    }
                }
            }
        }), System.Windows.Threading.DispatcherPriority.Background);
    }
}

private static T FindParent<T>(DependencyObject dependencyObject) where T : DependencyObject
{
    var parent = VisualTreeHelper.GetParent(dependencyObject);

    if (parent == null) return null;

    var parentT = parent as T;
    return parentT ?? FindParent<T>(parent);
}

private static T FindChild<T>(DependencyObject dependencyObject) where T : DependencyObject
{
    if (dependencyObject == null) return null;

    for (int i = 0; i < VisualTreeHelper.GetChildrenCount(dependencyObject); i++)
    {
        var child = VisualTreeHelper.GetChild(dependencyObject, i);

        var result = (child as T) ?? FindChild<T>(child);
        if (result != null) return result;
    }
    return null;
}
mm8
  • 163,881
  • 10
  • 57
  • 88
  • with KeyboardNavigation.TabNavigation="Cycle" the focus goes to the first element in the "visible" list but not to the next element in the real list – Alex Jul 03 '17 at 10:29
  • Works for me as per my example. Did you even try it? You should also read this: https://stackoverflow.com/help/how-to-ask – mm8 Jul 03 '17 at 10:31
  • Yes i tried these. Your example works. but in my projekt not evey item has a focusable control. See my not working example in my post – Alex Jul 03 '17 at 11:48