0

我正在尝试将行为应用于选择器继承者(ListView、ListBox 等),这将恢复所选元素并将其重点放在应用程序启动上。我还想在选项卡之间切换时聚焦选定的元素。

主要问题是找到 ListView 填充列表、完成所有内部活动并准备集中注意力的时间点。

我省略了关心 SelectedIndex 与持久性系统和清理代码的同步的行为部分,只显示了与控件交互的部分。

public class PersistSelectedItemIndexBehavior : Behavior<Selector>
{
  protected override void OnAttached()
  {
    base.OnAttached();

    RestorePersistedSelectedIndex();
    AssociatedObject.Loaded += AssociatedObject_OnLoaded;
  }

  private void AssociatedObject_OnLoaded(object sender, RoutedEventArgs e)
  {
    AssociatedObject.GotFocus += AssociatedObject_OnGotFocus;

    Dispatcher.BeginInvoke(DispatcherPriority.Input, new Action(() =>
    {
      AssosiatedObject.Focus();
      ScrollSelectedItemIntoView(
        AssociatedObject.FindVisualChild<ScrollViewer>());
    }));
  }

  private void AssociatedObject_OnGotFocus(object _, RoutedEventArgs __)
  {
    // do it only once on loading control
    AssociatedObject.GotFocus -= AssociatedObject_OnGotFocus;
    Dispatcher.BeginInvoke(DispatcherPriority.Input, new Action(
      SetFocusOnSelectedElement));
  }

  private void SetFocusOnSelectedElement()
  {
    var element = AssociatedObject.ItemContainerGenerator.ContainerFromItem(
        AssociatedObject.SelectedItem) as IInputElement;

    if (element != null)
      element.Focus();
  }

  private void ScrollSelectedItemIntoView(ScrollViewer scrollViewer)
  {
    double position = AssociatedObject.SelectedIndex;
    position -= scrollViewer.ViewportHeight / 2 - 1;
    scrollViewer.ScrollToVerticalOffset(position);
  }
}

当 ListView 中有几个项目时它可以工作,但如果有几百个项目,它就不会。何时ScrollSelectedItemIntoView调用Items.Countis0SelectedIndexis -1

我需要一个事件和状态的组合,这将清楚地表明 ListView 已准备好对其进行操作,已准备好滚动到所选元素并将其聚焦。

提前致谢

4

1 回答 1

0

最后我解决了一个问题。

public class PersistSelectedItemIndexBehavior : Behavior<Selector>
{
  protected override void OnAttached()
  {
    base.OnAttached();
    AssociatedObject.Loaded += AssociatedObject_OnLoaded;

    DependencyPropertyDescriptor.FromProperty(Selector.SelectedIndexProperty, AssociatedObject.GetType())
     .AddValueChanged(AssociatedObject, OnSelectedIndexChanged);

    RestorePersistedSelectedIndex();
  }

  private void OnSelectedIndexChanged(object sender, EventArgs e)
  {
    DependencyPropertyDescriptor.FromProperty(Selector.SelectedIndexProperty, AssociatedObject.GetType())
      .RemoveValueChanged(AssociatedObject, OnSelectedIndexChanged);

    Dispatcher.BeginInvoke(DispatcherPriority.Input, new Action(() =>
      ScrollSelectedItemIntoView(AssociatedObject.FindVisualChild<ScrollViewer>())));
  }

  private void AssociatedObject_OnLoaded(object sender, RoutedEventArgs e)
  {
    AssociatedObject.GotFocus += AssociatedObject_OnGotFocus;
  }

  private void AssociatedObject_OnGotFocus(object sender, RoutedEventArgs routedEventArgs)
  {
    AssociatedObject.GotFocus -= AssociatedObject_OnGotFocus;

    Dispatcher.BeginInvoke(DispatcherPriority.Input, (Action)SetFocusOnSelectedElement);
  }

  private void SetFocusOnSelectedElement()
  {
    var element = AssociatedObject.ItemContainerGenerator.ContainerFromItem(AssociatedObject.SelectedItem) as IInputElement;
    if (element != null)
      element.Focus();
  }

  private void ScrollSelectedItemIntoView(ScrollViewer scrollViewer)
  {
    double position = AssociatedObject.SelectedIndex;
    position -= scrollViewer.ViewportHeight / 2 - 1;
    scrollViewer.ScrollToVerticalOffset(position);

    Dispatcher.BeginInvoke(DispatcherPriority.Input, new Action(SetFocusOnSelectedElement));
  }
}
于 2013-11-04T11:08:42.643 回答