通过虚拟化的ItemControl / List进行选项卡

时间:2017-07-03 08:07:09

标签: c# wpf

我有一个虚拟化的ItemsControl,其中包含标签或文本框的列表。由于数据量的原因,需要虚拟化。现在我想通过键盘使用标签。这很有效,直到它到达可见列表的末尾。比焦点离开列表。有没有办法滚动"滚动"下一个可聚焦控件的列表?问题是并非列表中的每个项目都具有可聚焦控制。

以下是一个例子:

Here is an example:

有没有可行的解决方案?例如,可以加载10个以上的项目。或者找到最后加载的可聚焦项目并按代码滚动。或者在显示列表后,在后台添加所有数据。 RAM不是它是列表的生命周期的瓶颈。

我已经关注了这些Virtualizing an ItemsControl?

这是不可行的例子

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

背后的代码

 public List<string> testList {

        get
        {

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


    }

1 个答案:

答案 0 :(得分:1)

KeyboardNavigation.TabNavigation属性设置为Cycle,将容器的IsTabStop属性设置为false。这对我有用:

<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:如何在列表框中的项目之间进行选项卡: https://social.technet.microsoft.com/wiki/contents/articles/25152.wpf-how-to-tab-between-items-in-a-listbox.aspx

  

你的例子有效。但在我的项目中,不是evey项目具有可控制的控制权。

然后你将不得不写一些代码。例如,您可以处理容器的PreviewKeyDown事件:

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

以下是一些可以为您提供想法的示例代码:

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