DataGridViewで行のラインの色を変えるには?

解決


fumofumo  2009-01-14 22:22:06  No: 145653

WinXP(SP3) VB2005 Oracle9i で開発しています。

http://dobon.net/vb/dotnet/datagridview/rowpostpaint.html

のサンプルプログラムを流用して行の上に黒色の線を引いたのですが
垂直スクロールすると黒色の線が消える箇所があります。

水平スクロールバーが無い時は消えないのですが、
水平スクロールバーが有る時は消える現象が発生します。
原因がわからず困っています。
どなたかご教授願います。

-- 線を引く部分 --

Private Sub DataGridView1_RowPostPaint(ByVal sender As Object, _
ByVal e As System.Windows.Forms.DataGridViewRowPostPaintEventArgs) 
 Handles DataGridView1.RowPostPaint

Dim dgv As DataGridView = CType(sender, DataGridView)
'線の色を決定する
Dim linePen As Pen
linePen = Pens.Black
If e.RowIndex Mod 3 = 0 Then
    '線を引く位置を計算する
    Dim startX As Integer = IIf(dgv.RowHeadersVisible,
               dgv.RowHeadersWidth, 0)
    Dim startY As Integer = e.RowBounds.Top +
                e.RowBounds.Height - 22
    Dim endX As Integer = startX + _
       dgv.Columns.GetColumnsWidth(DataGridViewElementStates.Visible)
       - dgv.HorizontalScrollingOffset
    '線を引く 
    e.Graphics.DrawLine(linePen, startX, startY, endX, startY)
End If

End Sub


fumofumo  2009-01-21 02:41:05  No: 145654

垂直スクロールで下にスクロールした時は
マウスのホイールでもスクロールバーの
↓でも  DataGridView1_RowPostPaintで描画
した線は消えないのですが
マウスのホイールで上にスクロールすると
線が消えたり残っていたりします。
スクロールバーの↓で上にいった時は、
全ての線が消えてしまします。
なにか消えないような設定があるのでしょうか?
ご教授お願いします。

説明不足の部分があればご指摘お願い致します。


fumofumo  2009-01-21 16:59:20  No: 145655

追加事項

データの表示は DataGridViewにデータバインドしています。
よろしくお願いします。


ぽこ  2009-01-21 19:01:14  No: 145656

別の新しいプロジェクトに提示されたSubRoutineを含む簡単なアプリを
作って試してみても再現しますか?


魔界の仮面弁士  2009-01-21 22:04:28  No: 145657

> 簡単なアプリを作って試してみても再現しますか?
既に最低限のコードになっているように思えますけれども…。
ぽこさんの環境では再現しなかった、という事でしょうか?

> Dim startY As Integer = e.RowBounds.Top + e.RowBounds.Height - 22
この startY は、何の座標を意味する値ですか?

現在は、「上座標 + 行の高さ - 22」になっていますが、そもそも
「.Top + .Height」は「.Bottom」と同義ですので、これは「下座標 - 22」です。
この場合、それぞれの行の高さによっては、列ヘッダ内部の Y 座標に対して
描画される結果となる可能性もありますが、それで良いのでしょうか?

また、そもそも「22」とは、何を意味する値なのでしょうか。
(dgv.ColumnHeadersHeight に罫線サイズを考慮した値?)

> ↓でも  DataGridView1_RowPostPaintで描画
> した線は消えないのですが
いえ、描画されない可能性があります(理由は後述)。

> スクロールバーの↓で上にいった時は、
> 全ての線が消えてしまします。
↓で上にいく、というのが良く分かりませんが、それは置いといて。

現在の状況は、以下のようになっています。
==================================
(1) 前提条件
-----------------
RowPrePaint と RowPostPaint の間では、その行内のセル自体の描画処理が行われます。
CellPainting の独自描画処理で e.Handled = True を行っているならばまだしも、
単体で RowPrePaint を使っても、その結果は塗りつぶされてしまう事に注意してください。

今回、RowPrePaint 単体で罫線を描画できているように見えるのには、
RowPrePaint の描画処理が、「上の行」に被せる形で行われているためです。
(このため、描画処理の順番・タイミングの影響を受ける結果になっています)

そもそも RowPrePaint は、再描画が必要になった際に発生します。
「初回表示時」や「ページ単位で移動した場合」など、複数行の描画が
必要になった場合は、可視行を上から順に描画します。

上の行から順に処理されるため、上の行に被せる形で描画しても、
問題無く罫線が表示されたというわけですね。

==================================
(2) 下方向へのスクロール時の動作について
-----------------
スクロールバーの[▼]の場合、先頭1行が隠れて、末尾に1行増える事になりますよね。
そのため、新たに表示される末尾行のイベントが発生することになります。

また右端/下端のセルは、セルの一部が隠れている状態であることが多いため、
通常はその直前に「前回の末尾行」のイベントも発生します。

つまり、末尾 2 行のイベントが発生します。これも上から順に発生するため、
上の行に被せる形で描画しても、線が消える事は無かったのです。

もし、DataGridView の高さが1ドット違わず、末尾行全体が完全に表示される
状態であるならば、下へのスクロールに対しても罫線は描画される事がありません。
バーのつまみを素早くドラッグして、複数行の描画に持ち込めば描画されますけれども。

※これが、先に書いた“理由は後述”にあたります。

==================================
(3) 上方向へのスクロール時の動作について
-----------------
[▲]で上に移動した場合には、先頭に1行増えて、末尾行が隠れる事になります。
そのため、新たに表示される先頭行のイベントが発生することになりますが、
今回の実装では、描画された罫線を拝む事はできません。先に書いたように、
「描画済みの上の行に対して描画した場合」にしか表示されない実装に
なってしまっているためです。

ちなみに下端のセルは、先の『下スクロール』の説明にも書いたように、
セルの一部が隠されている可能性が高いわけですが、このときのクリッピングは
自動的に行われるため、『上スクロール』においては、先ほどのような
「新たな末尾行」のイベントは発生せず、先頭行のイベントのみが発生します。
==================================

> なにか消えないような設定があるのでしょうか?
セル本体の描画前に罫線を書くから消えてしまうわけです。
描画後すなわち RowPostPaint を使うようにすれば解決します。
また、他の行に対して描画してしまうと意味がないので、たとえば
  Dim startY As Integer = e.RowBounds.Top
などのように、領域内に収まるような座標を使うようにしてください。


特攻隊長まるるう  2009-01-21 22:42:02  No: 145658

> 描画後すなわち RowPostPaint を使うようにすれば解決します。
で、質問者が出しているコードが RowPostPaint なのでそのまま
実行して再現した場合はどしたらいいですか?(++

> また、そもそも「22」とは、何を意味する値なのでしょうか。
デフォルトでセルの高さが 21 っぽいから、サンプルと合わせて
イベントが起こった行の1つ前の行に描画しようとしているのが
原因っぽいです。上へスクロール時に後からイベントを起こした
行の描画処理に塗りつぶされてるんだと思います。
消えない場合があるのは、さらに後からイベント行の描画イベントが
起こっているとか?
イベントを起こした行のセル範囲内への描画に変更してみてください。


魔界の仮面弁士  2009-01-22 00:28:14  No: 145659

> 者が出しているコードが RowPostPaint なのでそのまま
あぅ。修正前の文章の方を投稿してしまいました。。。(;_;)
突っ込み大感謝。>特攻隊長まるるうさん

でも今更、長文の再投稿をしても鬱陶しいですよね…。

とりあえず、Pre/Post どちらのイベントを使っているにしても、
スクロール時のイベントの発生順の話や、その後に書いた
>> また、他の行に対して描画してしまうと意味がないので、
という問題点は一緒なので、先の発言を適当に読み替えてください。(泣)

要は、上の行位置に描画しているのが原因ということです。
  1) RowIndex = 2 のRowPrePaint
  2) RowIndex = 2 のコンテンツ
  3) RowIndex = 2 のRowPostPaint
  4) RowIndex = 3 のRowPrePaint
  5) RowIndex = 3 のコンテンツ
  6) RowIndex = 3 のRowPostPaint
と発生する処理の中で、それに対して、提示されたコードでは

  手順6 で RowIndex = 2 に描画(元質問)
  手順4 で RowIndex = 2 に描画(私の読み違い投稿)

となっていましたが、それを

  修正案1:手順 3 で、RowIndex = 2 に描画
  修正案2:手順 6 で、RowIndex = 3 に描画

のいずれかに修正しましょう、という事になります。

> デフォルトでセルの高さが 21 っぽいから、サンプルと合わせて
問題は、どのセルの高さを意図しているのか、という点になりますね。

fumofumo さんの書かれた
>> Dim startY As Integer = e.RowBounds.Top + e.RowBounds.Height - 22
の「22」が、もしも「現在行の高さ e.RowBounds.Height(初期値 21)」を
意図しているのだとしたら、そのコードは
  Dim startY As Integer = e.RowBounds.Top + e.RowBounds.Height - e.RowBounds.Height - 1
という事になります。

しかし、それならば最初から、
  Dim startY As Integer = e.RowBounds.Top - 1
と書くでしょうから、e.RowBounds.Height の可能性は排除して考えました。

そして近い値として、列ヘッダの高さ dgv.ColumnHeadersHeight(初期値 23) があるので、
  Dim startY As Integer = e.RowBounds.Top + e.RowBounds.Height - dgv.ColumnHeadersHeight + 1
の意図で書いたという可能性を思いついたのですが、いずれにしても、
fumofumo さんのコードの意図が不明瞭であったため、恐らく誤記であろうと類推し、
> また、そもそも「22」とは、何を意味する値なのでしょうか。
という逆質問をした次第です。


fumofumo  2009-01-22 17:27:42  No: 145660

皆さん返信ありがとうございます。

返信が遅くなり申し訳ありません。

Dim startY As Integer = e.RowBounds.Top + e.RowBounds.Height - 22

Dim startY As Integer = e.RowBounds.Top
で解決しました ^^;

返信を読みながら気が付いたのですが、
問題だけ書いて目的を書いていない質問でしたm(_ _)m
にもかかわらず多数の返信ありがとうございました。

目的はイベントを起こした行の上の部分に線を引きたかった。

http://dobon.net/vb/dotnet/datagridview/rowpostpaint.html

の「RowPostPaintイベントハンドラで描画する」
のサンプルで行の下に線が引かれていたコードが
Dim startY As Integer = e.RowBounds.Top + e.RowBounds.Height - 1
となっていたので -1を変更して行の上に線が引けるようにマイナスの
値を調整したのが -22 という数値です。

>ぽこさん
>別の新しいプロジェクトに提示されたSubRoutineを含む簡単なアプリを
>作って試してみても再現しますか?

再現しました。
これについては魔界の仮面弁士さんや特攻隊長まるるうさんの
ご指摘の通り
イベントを起こした行のセル範囲内への描画でなく
イベントが起こった行の1つ前の行に描画しようとしているのが
原因でした。

魔界の仮面弁士さん、特攻隊長まるるうさん
懇切丁寧なアドバスありがとうございました。
RowPostPaint イベントのことがわかりました。


※返信する前に利用規約をご確認ください。

※Google reCAPTCHA認証からCloudflare Turnstile認証へ変更しました。






  このエントリーをはてなブックマークに追加