それは一般的なことのように思えます-私たちのうち誰が臭いがしなかったのですか? また、 WPF ToolkitのDataGridコンポーネントは非常に実験的であるため、驚くべきことは何もありません。 しかし、何かが私を守った。
IndexOutOfRangeExceptionがDataGrid.csの行でドロップアウトしました
object nextItem = Items[nextRowIndex];
はい、nextRowIndexは実際には範囲外でした。-1でした。
それがどこから来たのか追跡しましょう。 それはもう少し高く発表されます:
int currentRowIndex = Items.IndexOf(CurrentItem);
int nextDisplayIndex = currentDisplayIndex;
int nextRowIndex = currentRowIndex;
そして、実行を取得する瞬間まで、その値(「右」ボタンの場合)は変わりません。
したがって、nextRowIndexの初期化に「ブレークダウン」を設定し、エディターでアクションを再現します... voila、currentRowIndex == -1!
何がありますか? Items.IndexOf()は、そのようなアイテムが存在しないことを示しています。 しかし、そうではないことを知っています。 ウォッチでこれを確認してください:
CurrentItemはコレクションの対応するItemと等しく、ここではすべてが公平です。 隣接する要素のインデックスは明確に定義されています。 しかし、具体的には、「私たちの」要素は見つかりませんでした。 さて、私たちはそれを理解します...
最初は、ItemCollection自体に疑いがあり、そのインスタンスはItemsです。
フレームワークに何らかの更新プログラムをインストールした後、WPFの一部のライブラリ(特に、関心のあるPresentationFramework.dll)がソースへのアプローチを停止したため、パスの一部をアセンブラリストで行う必要がありました。 しかし、それにもかかわらず、ItemCollectionは対応するCollectionViewの単なるラッパーであることがすぐにわかりました。 そして、この場合、これはBindingListCollectionViewであり、これはDataViewのデフォルトビューです。
ただし、BindingListCollectionView自体もラッパーであることが判明し、DataViewでIList.IndexOf()の呼び出しを送信しました。 彼が同じ「マイナス1」を返した後、私は問題がWPFの部分にあるという考えを捨てました。
それでは、IndexViewはDataViewでどのように機能し、なぜ目的の行が見つからないのですか?
コードの分析は、DataTableが行のインデックス(RB Tree)を使用することを示しました(そして、デバッガーでこれの前提条件を確認できます)。 また、DataViewは、目的のDataRowViewに関連付けられたDataRowを検索するテーブルのインデックスを参照します。
ただし、DataRowが編集モードの場合、すべての値のコピーを使用して、DataRow自体に一時レコードを作成します。
そして、現時点でのDataRowViewは一時レコードに正確に関連付けられていることがわかります(そうでなければ、その変更はDataRow自体に影響を与えないでしょう)、彼はインデックスでそれを探しています!
問題を再現するための小さな例を次に示します。
static void Main( string [] args)
{
var dataTable = new DataTable();
dataTable.Columns.Add( "Id" , typeof ( int ));
dataTable.Columns.Add( "Name" , typeof ( string ));
var row = dataTable.NewRow();
row[ "Id" ] = 1;
row[ "Name" ] = "John" ;
dataTable.Rows.Add(row);
row = dataTable.NewRow();
row[ "Id" ] = 2;
row[ "Name" ] = "Jack" ;
dataTable.Rows.Add(row);
var view = dataTable.DefaultView;
//row.BeginEdit();
Console .WriteLine(((IList)view).IndexOf(view[0]));
Console .WriteLine(((IList)view).IndexOf(view[1]));
}
* This source code was highlighted with Source Code Highlighter .
元の形式では、期待される0と1を取得します。
row.BeginEdit()のコメントを外すと、対応する行は停止します。
ちなみに、DataRowの代わりに対応するDataRowViewでBeginEdit()を呼び出すと、(一見)行インデックスが正しく表示されます。 これは、DataRowViewが遅延呼び出しを使用するという事実によるものです。関連するDataRowのBeginEdit()は、データが変更された場合にのみ呼び出されます。
まとめ
この動作がどのように正しいのかわかりません。 おそらくこれはバグですが、.NETチームのマジシャンがその機能を十分に発表する可能性があります。 いずれの場合でも、編集可能な文字列に対する非コア操作は避けてください。
または、LINQ2SQLやEntityFrameworkなどの新しいデータモデルを使用します。 しかし、あなたは他の驚きから免れていません:)
回避策として、Items.IndexOf(CurrentItem)の前にDataGrid.csでこれを追加しました。
// BUG: item not found if in edit mode
if (IsEditingRowItem)
CommitRowItem();
これに関して、ケースはクローズされたとみなすことができます。