如何在虚拟物品上设置标签导航?例如;
<ListBox x:Name="Items">
<ListBox.Template>
<ControlTemplate>
<ScrollViewer HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto">
<ItemsPresenter/>
</ScrollViewer>
</ControlTemplate>
</ListBox.Template>
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<VirtualizingStackPanel VirtualizingStackPanel.VirtualizationMode="Recycling"/>
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
<ListBox.ItemTemplate>
<DataTemplate>
<Button />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
如果我在Scrollviewer本身或Listbox父级等上设置TabNavigation = Once或Cycle,它只会查看视口中可用的项目,因为尚未生成其他项目。有人可能分享一个技巧,当通过项目对象进行选项卡时,它会允许Tab进入下一个尚未虚拟化的项目,同时将其带到视口中查看并通过控件提供直观的标签?
答案 0 :(得分:0)
所以基本上出现了什么(感谢额外的眼睛和另一个精美的开发者的帮助)是继续前进并渲染其他项目,但同时保持从onload虚拟化自定义行为,同时暴露依赖用于连续滚动并将当前项目置于视口中的视图中;
namespace The.Namespace
{
using System;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
/// <summary>
/// Scroll selected item into view.
/// </summary>
public class ListBoxFocusBehavior : FocusBehavior<ListBox>
{
public static readonly DependencyProperty IsContinuousProperty = DependencyProperty.Register("IsContinuous",
typeof(bool),
typeof(ListBoxFocusBehavior),
new PropertyMetadata(
false,
(d, e) => ((ListBoxFocusBehavior)d).IsContinuousScroll = (bool)e.NewValue));
/// <summary>
/// Gets or sets a value indicating whether this instance is continuous.
/// </summary>
/// <value>
/// <c>true</c> if this instance is continuous; otherwise, <c>false</c>.
/// </value>
public bool IsContinuous
{
get { return (bool)GetValue(IsContinuousProperty); }
set { SetValue(IsContinuousProperty, value); }
}
/// <summary>
/// Called after the behavior is attached to an AssociatedObject.
/// </summary>
protected override void OnAttached()
{
base.OnAttached();
AssociatedObject.SelectionChanged += SelectionChanged;
AssociatedObject.KeyDown += KeyDown;
}
/// <summary>
/// Keys down.
/// </summary>
/// <param name="sender">The sender.</param>
/// <param name="e">The <see cref="System.Windows.Input.KeyEventArgs"/> instance containing the event data.</param>
private void KeyDown(object sender, KeyEventArgs e)
{
e.Handled = false;
if (e.Key == Key.Tab && Keyboard.Modifiers == ModifierKeys.None)
{
//forward tab ...
var idx = AssociatedObject.Items.IndexOf(AssociatedObject.SelectedItem);
if (idx < AssociatedObject.Items.Count-1)
{
AssociatedObject.SelectedItem = AssociatedObject.Items[idx + 1];
e.Handled = true;
}
}
if (e.Key == Key.Tab && (Keyboard.Modifiers & ModifierKeys.Shift) == ModifierKeys.Shift)
{
//back tab.
var idx = AssociatedObject.Items.IndexOf(AssociatedObject.SelectedItem);
if (idx > 0)
{
AssociatedObject.SelectedItem = AssociatedObject.Items[idx - 1];
e.Handled = true;
}
}
}
/// <summary>
/// Called when the behavior is being detached from its AssociatedObject, but before it has actually occurred.
/// </summary>
protected override void OnDetaching()
{
base.OnDetaching();
AssociatedObject.SelectionChanged -= SelectionChanged;
AssociatedObject.KeyDown -= KeyDown;
}
/// <summary>
/// Gots the focus.
/// </summary>
/// <param name="sender">The sender.</param>
/// <param name="e">The <see cref="System.Windows.RoutedEventArgs"/> instance containing the event data.</param>
private void GotFocus(object sender, RoutedEventArgs e)
{
if (AssociatedObject.SelectedItem == null && AssociatedObject.Items.Any())
{
AssociatedObject.SelectedItem = AssociatedObject.Items.First();
}
}
/// <summary>
/// Selections the changed.
/// </summary>
/// <param name="sender">The sender.</param>
/// <param name="e">The <see cref="System.Windows.Controls.SelectionChangedEventArgs"/> instance containing the event data.</param>
private void SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (AssociatedObject.SelectedItem == null) return;
AssociatedObject.UpdateLayout();
//have to, otherwise the listbox will probably not focus.
Action setFocus = () =>
{
AssociatedObject.UpdateLayout();
AssociatedObject.ScrollIntoView(AssociatedObject.SelectedItem);
//ensure that if the container did not exist yet (virtualized), it gets created.
AssociatedObject.UpdateLayout();
var container =
AssociatedObject.ItemContainerGenerator.ContainerFromItem(
AssociatedObject.SelectedItem) as Control;
if (container != null)
{
container.Focus();
}
};
AssociatedObject.Dispatcher.BeginInvoke(setFocus);
}
}
}