C#でバックグラウンドでデータを更新しているときに、virtualmodedatagridviewがcellvalueneededを呼び出さないようにする方法

アダム

VirtualMode = trueのdatagridviewがあり、ユーザーがdatagridview内の行を並べ替えることができるようにドラッグアンドドロップも実装しています。私の問題は、SuspendLayout / ResumeLayoutを使用しているにもかかわらず、datagridviewが処理の途中でCellValueNeededを呼び出しているため、プログラムがクラッシュすることです。

DragDropイベント内に、次のコードがあります。「dragRow」はソース行で、「row」はドラッグ/ドロップイベントの宛先です。

gridview.SuspendLayout();
try
{
    // copy dragged row
    DataGridViewRow rowCopy = gridview.Rows[dragRow];
    DataValue dataCopy = dataList[dragRow];

    // remove dragged row
    dataList.RemoveAt(dragRow);
    gridview.Rows.RemoveAt(dragRow);

    // insert row
    dataList.Insert(row, dataCopy);
    gridview.Rows.Insert(row, rowCopy);

    // move selection to moved row
    gridview.CurrentCell = gridview[gridview.CurrentCell.ColumnIndex, row];
}
finally { gridview.ResumeLayout(true); }

ドラッグアンドドロップが開始される前に、私のプログラムはユーザーが最後の行を選択したことを検出します。ここに入らない理由で、最後の行は常に空になるように設計しました。通常、ユーザーが最後の行を選択すると、DragDropEffects.Copyオプションのみが有効な状態でドラッグアンドドロップが開始されます。最後から2番目の行も空であることが検出された場合は、ドラッグされている行を最後から2番目の行に切り替えて、ユーザーが空白の行を移動できるようにします(最後の行は移動できないため)。問題は、データリストから行が削除されてから新しい場所に挿入されるまでのDragDropイベント中に、datagridviewがCellValueNeededイベントを呼び出して、範囲外の例外でプログラムが何かを読み取ろうとしてクラッシュすることです。そこにない私のデータリスト。

表示されているツールチップに関連してこの問題も確認しました。ユーザーがドラッグした行/セル内でマウスをホバーすると、表示されるツールチップは、CellToolTipTextNeededイベントが間違ったセルに対して発生し、ResumeLayoutの後に更新されなかったかのように間違った行に対して表示されます。

仮想モードでデータソースを更新していることをdatagridviewに知らせるために、私が見逃していることがありますか?

参考までに、次のCellValueNeededハンドラーはdataList.RemoveAt(dragRow);、上記のコードの行の後にdataListに存在しなくなった行からgridviewが読み取ろうとしたためにIndexOutOfRangeExceptionがスローされる例です。

private void gridview_CellValueNeeded(object sender, DataGridViewCellValueEventArgs e)
{
    switch (e.ColumnIndex)
    {
        case 2: // Name
            e.Value = dataList[e.RowIndex].Name;
            break;
        case 3: // Value
            e.Value = dataList[e.RowIndex].Value;
            break;
    }
}
IVSoftware

あなたは2つの質問をします:

最初:Q:「C#でバックグラウンドでデータを更新しているときに、virtualmode datagridviewがcellvalueneededを呼び出さないようにするにはどうすればよいですか?」

A:私のモデリングは、この行がCellValueNeededを明示的に呼び出す原因であることを示しています。

gridview.CurrentCell = gridview[gridview.CurrentCell.ColumnIndex, row];

SuspendLayoutでラップしても、事実は変わりません。このメソッドでCellValueNeededが呼び出されないようにする場合は、この行を削除して別の場所で呼び出します。

2番目のQ:「仮想モードでデータソースを更新していることをdatagridviewに通知するために、何か足りないことがありますか?」

A :(短い答え)いいえ。

私のモデリングによると、次の場合、コードは例外をスローせずに機能します。

  • CellValueNeeded、アプリケーション、その他のウィンドウアクティビティ、またはマウスの状態の変化(コントロール上でのマウスの動きを含む)が原因であるかどうかに関係なく、何らかの理由でコントロールが再描画された場合に呼び出されることを考慮に入れています。

  • これらの3つの値は常に同期されており、行が削除または挿入されるとすぐに更新されます。

    • DGVのRowCount
    • データソースの数
    • AllowUserToAddRowsプロパティがtrueの場合、RowCountに必要なオフセット1。
  • コードのバグに対処します。intインデックス値を使用して行の削除と挿入を操作しようとしているということです。このアプローチはとにかく危険に満ちていますが、特にVirtualModeでは、これらのインデックス値とDataValueオブジェクトを含むソースリストとの間にバインディングないためです。表示されている方法でコーディングされたドラッグアンドドロップ操作では、リストアイテムを挿入または削除した瞬間に、これらのインデックス値の信頼性が低下します(つまり、機能しない可能性があります

投稿の最初のコードブロックをこれに置き換えてみて、問題が解決するかどうかをお知らせください。

try
{
    // We need to bank the actual objects here.
    DataValue
        draggedItem = dataList[dragRowIndex],
        dropTarget = dataList[dropRowIndex];

    // From here on out, anything index-based is doomed to 
    // spradically fail because we're changing the list by 
    // removing one or more items from it. There is no
    // binding between the two (you gave that up when you
    // set VirtualMode = true)

    dataList.RemoveAt(dragRowIndex); // Remove the dragged item(s)
    SynchronizeCounts();

    // vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
    // CRITICAL:
    // So at what index is the drop target now?
    int correctDropRowIndex = dataList.IndexOf(dropTarget);
    // In many cases it's not the same as dropRowIndex!!
    // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

    dataList.Insert(correctDropRowIndex, draggedItem);
    SynchronizeCounts();

    // move selection to moved row

    int safeColumnIndex = gridview.CurrentCell == null ? 0 : gridview.CurrentCell.ColumnIndex;
    int newIndexOfDroppedItem = dataList.IndexOf(draggedItem);

#if false
    // Setting CurrentCell is guaranteed to make calls
    // to CellValueChanged. You will have to do it elsewhere
    // if you don't want that to happen in this here code block.
    gridview.CurrentCell =
        gridview
        [
            columnIndex: safeColumnIndex,
            rowIndex: newIndexOfDroppedItem
        ];
#endif
}
catch (Exception e)
{
    Debug.Assert(false, e.Message);
}

... どこ ...

private void SynchronizeCounts()
{
    gridview.RowCount = dataList.Count;
    if (gridview.AllowUserToAddRows)
    {
        gridview.RowCount++;
    }
}

GitHubには、多くの診断機能を備えたDataGridViewVirtualModeの例があります。DGVのコード分析をさらに実行したい場合は、VisualStudioソリューション複製またはダウンロードしてください。

この記事はインターネットから収集されたものであり、転載の際にはソースを示してください。

侵害の場合は、連絡してください[email protected]

編集
0

コメントを追加

0

関連記事

Related 関連記事

ホットタグ

アーカイブ