我有一个虚拟化的ItemsControl,其中包含标签或文本框的列表。由于数据量的原因,需要虚拟化。现在我想通过键盘使用标签。这很有效,直到它到达可见列表的末尾。比焦点离开列表。有没有办法滚动"滚动"下一个可聚焦控件的列表?问题是并非列表中的每个项目都具有可聚焦控制。
以下是一个例子:
有没有可行的解决方案?例如,可以加载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;
}
}
答案 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;
}