1

2つのタブを含むTabControlを使用してWindowsフォームを設計しています。各タブにDataGridViewがあり、両方に同じ列(DataGridViewCheckBoxColumnを含む)が入力されていますが、すべてが1つのグリッドに含まれているのではなく、エンドユーザーが操作しやすいようにパラメーターが異なります。

DatagridViewごとにDataGridViewRowを設定でき、呼び出されたときにそれらを新しいインスタンスとして使用するだけだと思いましたが、そうではないようです。

次のコードは、ユーザーが最初のタブのDataGridViewのチェックボックスをオンまたはオフにしたときに正常に機能します。

private void dgvChq_CellValidating(object sender, DataGridViewCellValidatingEventArgs e)
    {
        int column = e.ColumnIndex;
        int row = e.RowIndex;
        if (column == 5)
        {
            DataGridViewCheckBoxCell c = dgvChq[e.ColumnIndex, e.RowIndex] as DataGridViewCheckBoxCell;
            if (c != null)
            {
                string a = e.FormattedValue.ToString();
                if (a == "True")
                {
                    using (SqlCommand cmd = con.CreateCommand())
                    {
                        cmd.CommandText = "UPDATE Customer.OrderHeader SET DateApproved = @approved WHERE OrderNumber = @ordNo";

                        cmd.Parameters.AddWithValue("@approved", DateTime.Today);
                        cmd.Parameters.AddWithValue("@ordNo",dgvChq.Rows[row].Cells[0].Value.ToString());

                        con.Open();
                        cmd.ExecuteNonQuery();
                        con.Close();
                    }
                    DataGridViewRow dr = dgvChq.SelectedRows[0];
                    dr.Cells[4].Value = DateTime.Today.ToString();
                }
                else
                {
                    using (SqlCommand cmd = con.CreateCommand())
                    {
                        cmd.CommandText = "UPDATE Customer.OrderHeader SET DateApproved = NULL WHERE OrderNumber = @ordNo";

                        cmd.Parameters.AddWithValue("@ordNo", dgvChq.Rows[row].Cells[0].Value.ToString());

                        con.Open();
                        cmd.ExecuteNonQuery();
                        con.Close();
                    }
                    DataGridViewRow dr = dgvChq.SelectedRows[0];
                    dr.Cells[4].Value = "";
                }
            }
        }
    }

そのため、別のDataGridViewRowを使用して、このコードを他のタブの他のDataGridViewに再利用できると考えましたが、失敗します。

private void dgvCredit_CellValidating(object sender, DataGridViewCellValidatingEventArgs e)
    {
        int column = e.ColumnIndex;
        int row = e.RowIndex;
        if (column == 0)
        {
            DataGridViewCheckBoxCell c = dgvCredit[e.ColumnIndex, e.RowIndex] as DataGridViewCheckBoxCell;
            if (c != null)
            {
                string a = e.FormattedValue.ToString();
                if (a == "True")
                {
                    using (SqlCommand cmd = con.CreateCommand())
                    {
                        cmd.CommandText = "UPDATE Customer.OrderHeader SET DateApproved = @approved WHERE OrderNumber = @ordNo";

                        cmd.Parameters.AddWithValue("@approved", DateTime.Today);
                        cmd.Parameters.AddWithValue("@ordNo", dgvCredit.Rows[row].Cells[1].Value.ToString());

                        con.Open();
                        cmd.ExecuteNonQuery();
                        con.Close();
                    }
                    DataGridViewRow dgvr = dgvCredit.SelectedRows[0];
                    dgvr.Cells[4].Value = DateTime.Today.ToString();
                }
                else
                {
                    using (SqlCommand cmd = con.CreateCommand())
                    {
                        cmd.CommandText = "UPDATE Customer.OrderHeader SET DateApproved = NULL WHERE OrderNumber = @ordNo";

                        cmd.Parameters.AddWithValue("@ordNo", dgvCredit.Rows[row].Cells[1].Value.ToString());

                        con.Open();
                        cmd.ExecuteNonQuery();
                        con.Close();
                    }
                    DataGridViewRow dgvr = dgvCredit.SelectedRows[0];
                    dgvr.Cells[4].Value = "";
                }
            }
        }
    }

ここで何かを見逃したことがありますか、または同じフォームで複数のDataGridViewRowsを使用することはできませんか?

たぶん、私もこれを行うことができるより「動的な」方法があり、それは私の問題を解決するのに役立つかもしれませんか?

PS:チェックボックスがクリックされたときに2番目のdatagridviewrow(dgvr)がまだnullとして表示されているようです。そのため、ここでエラーが発生しています。

4

1 に答える 1

1

@ Keefa2011、問題はDataGridViewRowインスタンスではありません。それらは参照型であることに同意したので、そのようなインスタンスをメンバー変数にするときは注意が必要です。ただし、コードでは、それらのスコープは特定のイベントに限定されています。

問題はあなたが言及した行にあります。FullRowSelectが有効になっていない限り、That's'cozで常に例外が発生し、DataGridViewRow dgvr = dgvCredit.SelectedRows[0];選択された行数は常にゼロになります。あなたはそれを処理する必要があります。これを行うには、行全体の選択を有効にするか、誤った1行を変更します(以下を参照)。2番目のdgvでのみエラーが発生する理由がわかりません。おそらく、最初のdgvで全行選択が有効になっていますか?

さらに、あなたのコードはリファクタリングを求めています。あなたが2ページに書いたことは、実際にはこれだけです。

private void dgvChq_CellValidating(object sender, 
                                   DataGridViewCellValidatingEventArgs e)
{
    if (e.ColumnIndex != 5)
        return;

    HandleCheckedChanged(dgvChq, e, 
                         Convert.ToInt32(dgvChq.Rows[e.RowIndex].Cells[0].Value));
}

private void dgvCredit_CellValidating(object sender, 
                                      DataGridViewCellValidatingEventArgs e)
{
    if (e.ColumnIndex != 0)
        return;

    HandleCheckedChanged(dgvCredit, e, 
                         Convert.ToInt32(dgvCredit.Rows[e.RowIndex].Cells[1].Value));
}

private void HandleCheckedChanged(DataGridView dgv, 
                                  DataGridViewCellValidatingEventArgs e, int id)
{
    object toBeDisplayedDateValue = (bool)e.FormattedValue ? (DateTime?)DateTime.Today : null;

    using (SqlCommand cmd = con.CreateCommand())
    {
        cmd.CommandText = @"UPDATE Customer.OrderHeader 
                            SET    DateApproved = @approvedDate 
                            WHERE  OrderNumber = @ordNo";

        cmd.Parameters.AddWithValue("@approvedDate", toBeDisplayedDateValue);
        cmd.Parameters.AddWithValue("@ordNo", id);

        con.Open();
        cmd.ExecuteNonQuery();
        con.Close();
    }

    dgv.Rows[e.RowIndex].Cells[4].Value = toBeDisplayedDateValue; //you could just do 
                                                                  //this much 
    //or

    //DataGridViewRow dgvr = dgv.SelectedRows[0]; //this line works only if there 
                                                  //is at least one selected row when 
                                                  //validating cell. For this you 
                                                  //require 
                                                  //dgv.SelectionMode = DataGridViewSelectionMode.FullRowSelect
}

よりクリーンな設計を行うには、実際にデータベース操作を別のクラスに移動する必要があります。コードに関する最大の注意点は、IDをとして渡す場所です"@ordNo"。私はそれを関数の外に移動しました'cozUI側からその値を取得する必要があるかどうかはわかりません。そのロジックはdb側から処理する必要があります。つまり、注文番号を別の場所に保持する必要があります。おそらく、dgv行のタグなどです。グリッドビューセルの値が実際にデータベース内の適切なレコードとして考慮されることが確実である場合は、先に進み、引数として注文番号列の列インデックスのみを渡す必要があります。

もう1つ、セルからフォーカスを移動するたびに発生するため、実際にはセル検証イベントが必要だとは思いません。そのdb操作はコストがかかる可能性があるため、セルの値が変更された場合にのみ操作を実行するセル値変更イベントを登録できます。ここで注意すべきことの1つは、すべてのセルを完全にロードした後でのみイベントを登録する必要があるということです。そうしないと、最初にレコードにデータを入力した場合でも起動されます。

例えば、

private void Form1_Load(object sender, EventArgs e)
{
    //---------------------------------------------
    // load dgv...
    //---------------------------------------------

    dgvChq.CellValueChanged += dgvChq_CellValueChanged;
    dgvCredit.CellValueChanged += dgvCredit_CellValueChanged;
}

private void dgvChq_CellValueChanged(object sender, DataGridViewCellEventArgs e)
{
    if (e.ColumnIndex != 5)
        return;

    HandleCheckedChanged(dgvChq, e, 0);
}

private void dgvCredit_CellValueChanged(object sender, DataGridViewCellEventArgs e)
{
    if (e.ColumnIndex != 0)
        return;

    HandleCheckedChanged(dgvCredit, e, 1);
}

private void HandleCheckedChanged(DataGridView dgv, DataGridViewCellEventArgs e, 
                                  int columnIndexOfOrderNo)
{
    DataGridViewCheckBoxCell c = dgv[e.ColumnIndex, e.RowIndex] as DataGridViewCheckBoxCell;
    object toBeDisplayedDateValue = (bool)c.EditedFormattedValue ? (DateTime?)DateTime.Today : null;

    using (SqlCommand cmd = con.CreateCommand())
    {
        cmd.CommandText = @"UPDATE Customer.OrderHeader 
                            SET    DateApproved = @approvedDate 
                            WHERE  OrderNumber = @ordNo";

        cmd.Parameters.AddWithValue("@approvedDate", toBeDisplayedDateValue);
        cmd.Parameters.AddWithValue("@ordNo", 
                                    Convert.ToInt32(dgv.Rows[e.RowIndex].Cells[columnIndexOfOrderNo].Value));

        con.Open();
        cmd.ExecuteNonQuery();
        con.Close();
    }

    dgv.Rows[e.RowIndex].Cells[4].Value = toBeDisplayedDateValue;
}

理想的には、更新するフィールド(この場合は4)のcolumnindexを関数に渡す必要があります。これらは、将来dgvで簡単に異なる可能性があるためです。

于 2012-07-27T18:56:22.633 に答える