DataViewで行が失われた場合

データベーステーブルのWPFエディターをデバッグしているときに、キーボードを使用して隣接するセルを切り替えると例外が発生しました。

それは一般的なことのように思えます-私たちのうち誰が臭いがしなかったのですか? また、 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();






これに関して、ケースはクローズされたとみなすことができます。






All Articles