1

選択したアイテムを両方向にバインドできるように作成SelectionHelperしました。 このヘルパーでは、ビューモデルから選択が変更されたときに、最初に選択された項目を呼び出します。呼び出しは正常に返されます。しかし、後で UI のメッセージ キューのどこかで何かが発生し、コレクションの IndexOf が呼び出されます。 UI の仮想化のため、非同期であると思われます。絶対に知りたいアイテムのインデックスです。しかし、なぜアイテムの代わりに置くのか理解できません。 これはバグですか、それとも文書化されていない機能ですか?DataGrid
ScrollIntoView
DataGridItemsControl.ItemInfo

私のコレクションはこれらのインターフェースを実装しています: IList<T>IListINotifyCollectionChanged

のコードは次の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、2つのオーバーロードがありOnBringItemIntoViewます。1つはタイプ用ItemsControl.ItemInfo、もう1つは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の返事を待ちます。

于 2013-01-10T15:56:51.880 に答える