我为SelectionHelper
创建DataGrid
,允许双向绑定所选项目
在此帮助器中,当选择从viewmodel更改时,我为第一个选定项目调用ScrollIntoView
。呼叫成功返回。但后来在UI的消息队列中发生了某些事情,并调用了我的集合的IndexOf
由于UI虚拟化,我怀疑它是异步的。 DataGrid
绝对是了解项目索引的重要信息。但是我无法理解为什么它会将ItemsControl.ItemInfo
代替项目
这是一个bug还是未记录的功能?
我的收藏实现了这些界面: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);
}
更新 问题已在此update
中修复答案 0 :(得分: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)
iside 的原因
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的answer。