0

如何在虚拟化项目上设置选项卡导航?例如;

        <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 继续到下一个尚未虚拟化的项目,同时将其带到视口中查看并通过控件提供直观的选项卡?

4

1 回答 1

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);
        }
    }
}
于 2013-01-03T21:08:51.630 回答