DOBON.NETプログラミング道掲示板

■34848 / 親記事)  DataTableのDataRowVersion毎のInt型値の比較がうまくいかない
  
□投稿者/ 奥琵琶湖 一般人(1回)-(2021/07/27(Tue) 09:06:57)
  • アイコン環境/言語:[C#] 
    分類:[.NET] 

    DataTableをバインドしたDatagridviewのセルのスタイルを、値の状態によって変更しています。
    値に変更があるときは文字色を青色に変更したいので、以下のコードをDatagridview1のCellFormattingのイベントハンドラに書いています。

    DataTable1はDatagridview1にバインドしています。DataTable1は数値列と文字列を持っています。

    DataRow dtrow = DataTable1.Rows[e.RowIndex];
    if (dtrow.RowState == DataRowState.Modified
    && dtrow[e.ColumnIndex, DataRowVersion.Original] != dtrow[e.ColumnIndex,DataRowVersion.Current])
    {
    e.CellStyle.ForeColor = Color.Blue;
    }

    dtrow[e.ColumnIndex]の値がstring型の場合は想定通りの動きをするのですが、int型の場合はなぜか常にdtrow[e.ColumnIndex, DataRowVersion.Original] != dtrow[e.ColumnIndex,DataRowVersion.Current]がTrueになります。
    そのため、行の他の値に変更があると、数値列は変更がなくても青色に変わってしまいます。
    dtrow[e.ColumnIndex, DataRowVersion.Original]とdtrow[e.ColumnIndex, DataRowVersion.Current]の値と型をConsoleに書き出して同じ値になっていても、上記式がTrueになります。

    Tostring()してからの比較に書き換えることで、とりあえず回避はできたのですが、なぜこういう動きになるのか理解できません。
    if (dtrow.RowState == DataRowState.Modified
    && dtrow[e.ColumnIndex, DataRowVersion.Original].ToString() != dtrow[e.ColumnIndex,DataRowVersion.Current].ToString())

    どなたかお分かりでしたら、原因をご教授いただけませんか。よろしくお願いいたします。
マルチポストを報告
違反を報告
引用返信 削除キー/
■34850 / ResNo.1)  Re[1]: DataTableのDataRowVersion毎のInt型値の比較がうまくいかない
□投稿者/ 魔界の仮面弁士 大御所(1378回)-(2021/07/27(Tue) 10:53:17)
  • アイコンNo34848に返信(奥琵琶湖さんの記事)
    > 値に変更があるときは文字色を青色に変更したいので、
    これでどうでしょう。

    private void dataGridView1_CellFormatting(object sender, DataGridViewCellFormattingEventArgs e)
    {
     var dgv = (DataGridView)sender;
     var col = dgv.Columns[e.ColumnIndex];
     var row = dgv.Rows[e.RowIndex];
     DataRow dtrow = (row.DataBoundItem as DataRowView)?.Row;
     if (!col.IsDataBound || dtrow?.RowState != DataRowState.Modified)
     {
      return;
     }

     object oldValue = dtrow[col.DataPropertyName, DataRowVersion.Original];
     object curValue = dtrow[col.DataPropertyName, DataRowVersion.Current];
     if (oldValue?.GetType() != curValue.GetType() || System.Collections.Comparer.Default.Compare(oldValue, curValue) != 0)
     {
      // 文字色が黒→青の変化だけだと、違いが分かりにくいのと、
      // 文字色だけでは「空文字列にした場合」と「セル選択時」の問題があるので
      // 背景色や選択色も合わせて変更しています。
      e.CellStyle.ForeColor = Color.Red;
      e.CellStyle.BackColor = Color.LightBlue;
      e.CellStyle.SelectionForeColor = Color.Yellow;
      e.CellStyle.SelectionBackColor = Color.DarkBlue;
     }
    }


    > Datagridview1にバインドしています
    dataGridView1 でも
    DataGridView1 でもなく、
    Datagridview1 表記というのは珍しいですね。


    > が Trueになります。
    C# なので、普通は True ではなく true と書く所ですね。
    bool.TrueString は "True" を返すので、True 表記でも間違いとは言えませんが。


    > Int型値の比較がうまくいかない
    C# なので、普通は int あるいは Int32 と書く所ですね。

    VB 開発者が相手なら、大文字小文字の違いは無視されるので構いませんが、
    C# や JavaScript は区別する言語なので、正しく書き分けましょう。


    > Tostring()してからの比較に書き換えることで、
    Tostring() ではなく
    ToString() ですよね。


    文字列による比較を用いる場合、「同じ値を示す別の文字列」が
    異なる値と見做される可能性がある点に注意してください。
    具体的には、「DBNull と空文字列」や「小数値同士」などです。

    decimal d1 = 1.00M;
    decimal d2 = 1.0M;

    // false
    bool a = d1.ToString() == d2.ToString();

    // true
    bool b = d1 == d2;
    bool c = System.Collections.Comparer.Default.Compare(d1, d2) == 0;




    > DataRow dtrow = DataTable1.Rows[e.RowIndex];

    上記の取得方法はお奨めしません。
    DataGridView 上の行の並びと、DataTable 上の行の並びは必ずしも一致しないからです。

    具体的には、DataGridView の列ヘッダークリックによるソート処理や、
    DataView や BindingSource によるフィルター処理を伴った場合ですね。

    そのため、バインド元の DataRow を得る場合には、
     DataRow dtrow = (((DataGridView)sender).Rows[e.RowIndex].DataBoundItem as DataRowView)?.Row;
    のようにした方が確実です。

    これは列に対してもいえる事です。
    e.ColumnIndex が必ずしも DataTable 上の列番号と一致するわけではありません。
    同じ列を書式をかえて複数の列にバインドしたり、一部の列をバインドしなかったり、
    バインドしない列を追加することもあるからです。


    > dtrow[e.ColumnIndex, DataRowVersion.Original] != dtrow[e.ColumnIndex,DataRowVersion.Current]

    これが期待動作しないのは、比較式の両辺が object 型であることが原因です。

    object o1 = 1;
    object o2 = 1;

    // false
    bool a = o1 == o2;
    bool b = object.ReferenceEquals(o1, o2);

    // true
    bool c = object.Equals(o1, o2);
    bool d = int.Equals(o1, o2);
    bool e = (dynamic)o1 == (dynamic)o2;
    bool f = System.Collections.Comparer.Default.Compare(o1, o2) == 0;
違反を報告
引用返信 削除キー/
■34851 / ResNo.2)  Re[2]: DataTableのDataRowVersion毎のInt型値の比較がうまくいかない
□投稿者/ 奥琵琶湖 一般人(2回)-(2021/07/27(Tue) 11:38:28)
  • アイコンNo34850に返信(魔界の仮面弁士さんの記事)
    魔界の仮面弁士様

    ご丁寧な説明をありがとうございます。また、表記の間違いが多く申し訳ありませんでした。
    希望通りの動きを実現できました。

    DataGridViewとDataTableの列行ずれの問題も、気になっていたのでご指摘いただき大変助かりました。
    (対処療法的に別関数を挟んで変換していました)

    ありがとうございました。

違反を報告
引用返信 削除キー/
■34852 / ResNo.3)  Re[3]: DataTableのDataRowVersion毎のInt型値の比較がうまくいかない
□投稿者/ 奥琵琶湖 一般人(4回)-(2021/07/27(Tue) 11:44:11)
  • アイコン解決済みを押し忘れました。ありがとうございました。
解決み!
違反を報告
引用返信 削除キー/
■34853 / ResNo.4)  Re[2]: DataTableのDataRowVersion毎のInt型値の比較がうまくいかない
□投稿者/ 魔界の仮面弁士 大御所(1379回)-(2021/07/27(Tue) 14:48:59)
  • アイコンNo34850に訂正(魔界の仮面弁士の記事)
    > 文字列による比較を用いる場合、「同じ値を示す別の文字列」が
    > 異なる値と見做される可能性がある点に注意してください。
    > 具体的には、「DBNull と空文字列」や「小数値同士」などです。

    済みません、ここの説明がおかしいですね。

    decimal の例は「同じ値として扱われるが、別の文字列となる」パターンで
    DBNull と空文字列は「異なる値であるが、同じ文字列となる」パターンです。


    その他、object 型同士の比較の場合、
    (byte)0 と (int)0 を同一視するかどうかとか、
    "" と null と DBNull を同一視するかどうかといった問題もありえます。

    # 今回のケースでは気にする必要が無さそうなので、
    # 解決済みチェックはつけたままにしておきます。
解決み!
違反を報告
引用返信 削除キー/
■34854 / ResNo.5)  Re[3]: DataTableのDataRowVersion毎のInt型値の比較がうまくいかない
□投稿者/ 奥琵琶湖 一般人(5回)-(2021/07/27(Tue) 16:01:37)
  • アイコンNo34853に返信(魔界の仮面弁士さんの記事)
    魔界の仮面弁士様

    詳細なご説明をありがとうございます。
    今回の場合は数値列の値はint型かつ必ず0以上の値が入るので回避できそうです。

    ありがとうございました。
解決み!
違反を報告
引用返信 削除キー/



スレッド内ページ移動 / << 0 >>

このスレッドに書きこむ

Mode/  Pass/


- Child Tree -