1

我创建SelectionHelperDataGrid它允许在两个方向上绑定所选项目。
在此辅助手册中,我ScrollIntoView在从ViewModel的选择更改时呼叫第一个选定的项目。通话成功返回。但是稍后在 UI 的消息队列中的某个地方发生了一些事情,并且调用了我的集合的 IndexOf。
由于 UI 虚拟化,我怀疑它是异步的。DataGrid绝对是想知道项目的索引。但是我无法理解为什么它会放置ItemsControl.ItemInfo而不是 item。
这是错误还是未记录的功能?

我的集合实现了这些接口:IList<T>, IList,INotifyCollectionChanged

这是代码IndexOf

public int IndexOf(object value)
        {
            if ((value != null && !(value is T))
                || (value == null && typeof(T).IsValueType))
                throw new ArgumentException(WrongTypeMessage, "value");

            return IndexOf((T)value);
        }

它按预期抛出异常=)

更新

是的,我的猜测是正确的。这是 DataGrid 的代码ScrollIntoView

public void ScrollIntoView(object item)
    {
      if (item == null)
        throw new ArgumentNullException("item");
      this.ScrollIntoView(this.NewItemInfo(item, (DependencyObject) null, -1));
    }

    internal void ScrollIntoView(ItemsControl.ItemInfo info)
    {
      if (this.ItemContainerGenerator.Status == GeneratorStatus.ContainersGenerated)
        this.OnBringItemIntoView(info);
      else
        this.Dispatcher.BeginInvoke(DispatcherPriority.Loaded, (Delegate) new DispatcherOperationCallback(((ItemsControl) this).OnBringItemIntoView), (object) info);
    }

更新 问题已在此更新中修复

4

1 回答 1

2

是的,可能我找到了原因:

这是 DataGrid.ScrollIntoView 的代码(由 Resharper 反编译)

internal void ScrollIntoView(ItemsControl.ItemInfo info)
    {
     if (this.ItemContainerGenerator.Status == GeneratorStatus.ContainersGenerated)
        this.OnBringItemIntoView(info);
     else
        this.Dispatcher.BeginInvoke(DispatcherPriority.Loaded, (Delegate) new DispatcherOperationCallback(((ItemsControl) this).OnBringItemIntoView), (object) info);
    }

在这里,它们info转换为对象类型。嗯,问题确实是DispatcherOperationCallback预期的object
但是ItemsControl有两个重载OnBringItemIntoView:一个用于类型ItemsControl.ItemInfo,第二个用于object类型。

internal object OnBringItemIntoView(ItemsControl.ItemInfo info)
    {
      FrameworkElement frameworkElement = info.Container as FrameworkElement;
      if (frameworkElement != null)
        frameworkElement.BringIntoView();
      else if ((info = this.LeaseItemInfo(info, true)).Index >= 0)
      {
        VirtualizingPanel virtualizingPanel = this.ItemsHost as VirtualizingPanel;
        if (virtualizingPanel != null)
          virtualizingPanel.BringIndexIntoView(info.Index);
      }
      return (object) null;
    }

internal object OnBringItemIntoView(object arg)
    {
     return this.OnBringItemIntoView(this.NewItemInfo(arg, (DependencyObject) null, -1));
    }

猜猜,哪一个被选中了?;-)
所以他们被ItemInfo包裹在ItemInfo. 这就是this.LeaseItemInfo(info, true)为什么

internal object OnBringItemIntoView(ItemsControl.ItemInfo info)
    {
      FrameworkElement frameworkElement = info.Container as FrameworkElement;
      if (frameworkElement != null)
        frameworkElement.BringIntoView();
      else if ((info = this.LeaseItemInfo(info, true)).Index >= 0)
      {
        VirtualizingPanel virtualizingPanel = this.ItemsHost as VirtualizingPanel;
        if (virtualizingPanel != null)
          virtualizingPanel.BringIndexIntoView(info.Index);
      }
      return (object) null;
    }

收到不正确的项目并IndexOf以错误的值调用:

 internal ItemsControl.ItemInfo LeaseItemInfo(ItemsControl.ItemInfo info, bool ensureIndex = false)
    {
      if (info.Index < 0)
      {
        info = this.NewItemInfo(info.Item, (DependencyObject) null, -1);
        if (ensureIndex && info.Index < 0)
          info.Index = this.Items.IndexOf(info.Item);
      }
      return info;
    }

但这ScrollIntoView对于未生成容器的所有情况都应该中断。ScrollIntoView简单的解决方法是通过调用Dispatcher.BeginInvoke

我会等待MS的答复

于 2013-01-10T15:56:51.880 回答