C# と VB.NET の質問掲示板

ASP.NET、C++/CLI、Java 何でもどうぞ

C# と VB.NET の入門サイト

Re[4]: DataGridViewのセル結合の描画について


(過去ログ 122 を表示中)

[トピック内 5 記事 (1 - 5 表示)]  << 0 >>

■73186 / inTopicNo.1)  DataGridViewのセル結合の描画について
  
□投稿者/ てくてく (1回)-(2014/08/26(Tue) 16:49:06)

分類:[VB.NET/VB2005 以降] 

分類:[VB.NET/VB2005 以降] 

OS:Windows XP
言語:Visual Basic
DB: SQL Server 2008R2 
開発環境:Visual Studio 2008 

てくてくと申します。よろしくお願いします。

WindowsFormにDataGridViewを貼り付けて、以下のような一覧表を作成しています。

|−−−−|−−−−|−−−−|
|大項目a |中項目a |小項目a |
|	 | 	  |小項目b |
|	 | 	  |小項目c |
|	 | 	  |小項目d |
|	 | 	  |小項目e |
|	 | 	  |小項目f |
|	 |−−−−|−−−−|
|        |中項目b |小項目a |
|	 | 	  |小項目b |
|	 | 	  |小項目c |
|	 | 	  |小項目d |
|	 | 	  |小項目e |
|	 | 	  |小項目f |
|        |−−−−|−−−−|
|        |中項目c |小項目a |
|	 | 	  |小項目b |
|	 | 	  |小項目c |
|	 | 	  |小項目d |
|	 | 	  |小項目e |
|	 | 	  |小項目f |
|−−−−|−−−−|−−−−|
|大項目b |中項目a |小項目a |
|	 | 	  |小項目b |
|	 | 	  |小項目c |
|	 | 	  |小項目d |
|	 | 	  |小項目e |
|	 | 	  |小項目f |
|        |−−−−|−−−−|
|        |中項目b |小項目a |
|	 | 	  |小項目b |
|	 | 	  |小項目c |
|	 | 	  |小項目d |
|	 | 	  |小項目e |
|	 | 	  |小項目f |
|        |−−−−|−−−−|
|        |中項目c |小項目a |
|	 | 	  |小項目b |
|	 | 	  |小項目c |
|	 | 	  |小項目d |
|	 | 	  |小項目e |
|	 | 	  |小項目f |
|−−−−|−−−−|−−−−|
|特殊項目a                 |
|−−−−−−−−−−−−−|
|特殊項目b                 |
|−−−−−−−−−−−−−|
|特殊項目c                 |
|−−−−−−−−−−−−−|

※「大項目a」〜「大項目c」は18行づつを結合、「中項目a」〜「中項目c」は6行づつを結合、「小項目a」〜「小項目f」と「特殊項目a」〜「特殊項目c」は1行


ひとまず自力でセルを描画する事で、上図のようなグリッドは作成できたのですが、スクロールバーで動かすと描画が正しくできません。
詳しく説明すると、一番下までスクロールした後に、スクロールバーの上矢印をクリックしていくと「大項目a」と「中項目c」が1行づつの描画になってしまいます。<図1>
また、そのまま上矢印をクリックしてき、「中項目c」の最上行までくると「中項目c」が正しく描画されます。<図2>
「大項目a」も同じように最上行までくると正しく描画されます。<図3>
さらに、「Alt+Tab」で表示切替した場合は、

<図1>
|−−−−|−−−−|小項目e |
|−−−−|−−−−|小項目f |
|−−−−|−−−−|−−−−|
|大項目b |中項目a |小項目a |
|	 | 	  |小項目b |
|	 | 	  |小項目c |
|	 | 	  |小項目d |
|	 | 	  |小項目e |
|	 | 	  |小項目f |
|        |−−−−|−−−−|

<図2>
|−−−−|−−−−|−−−−|
|−−−−|中項目c |小項目a |
|−−−−| 	  |小項目b |
|−−−−| 	  |小項目c |
|−−−−| 	  |小項目d |
|−−−−| 	  |小項目e |
|−−−−| 	  |小項目f |
|−−−−|−−−−|−−−−|
|大項目b |中項目a |小項目a |
|	 | 	  |小項目b |
|	 | 	  |小項目c |

<図3>
|−−−−|−−−−|−−−−|
|大項目a |中項目a |小項目a |
|	 | 	  |小項目b |
|	 | 	  |小項目c |
|	 | 	  |小項目d |
|	 | 	  |小項目e |
|	 | 	  |小項目f |
|	 |−−−−|−−−−|
|        |中項目b |小項目a |
|	 | 	  |小項目b |
|	 | 	  |小項目c |
|	 | 	  |小項目d |


ソースは以下のように組んでいます。
長々と申し訳ないですが、是非とも御教授お願いします。

    Private Sub DataGridView1_CellPainting(ByVal sender As System.Object, ByVal e As System.Windows.Forms.DataGridViewCellPaintingEventArgs) Handles DataGridView1.CellPainting

        Dim xDgv As DataGridView = DirectCast(sender, DataGridView)
        Dim xRect As Rectangle
        Dim xCell As DataGridViewCell
        Dim xHeaderText As String = String.Empty

        If e.ColumnIndex = 0 Then ' 1列目の処理

            xRect = e.CellBounds

            If e.RowIndex Mod 18 = 0 And e.RowIndex < 36 Then

                xRect = e.CellBounds
                xCell = DataGridView1(e.ColumnIndex, e.RowIndex)

                'セルの高さを足す
                xRect.Height += xCell.Size.Height * 17

                ' セルボーダーライン分矩形の位置を補正
                xRect.X -= 1
                xRect.Y -= 1

                ' 背景、セルボーダーラインを描画
                e.Graphics.FillRectangle(New SolidBrush(DataGridView1.ColumnHeadersDefaultCellStyle.BackColor), xRect)
                e.Graphics.DrawRectangle(New Pen(xDgv.GridColor), xRect)

                If e.RowIndex = 0 Then
                    xHeaderText = "大項目a"
                Else
                    xHeaderText = "大項目b"
                End If

                ' セルの値を描画
                TextRenderer.DrawText(e.Graphics, xHeaderText, e.CellStyle.Font, xRect, e.CellStyle.ForeColor, TextFormatFlags.HorizontalCenter Or TextFormatFlags.VerticalCenter)

            ElseIf e.RowIndex >= 36 Then

                xRect = e.CellBounds
                xCell = DataGridView1(e.ColumnIndex, e.RowIndex)

                ' セル2つ分の幅を足す
                xRect.Width += xCell.Size.Width * 2
                xRect.X -= 1
                xRect.Y -= 1

                ' 背景、セルボーダーラインを描画
                e.Graphics.FillRectangle(New SolidBrush(DataGridView1.ColumnHeadersDefaultCellStyle.BackColor), xRect)
                e.Graphics.DrawRectangle(New Pen(xDgv.GridColor), xRect)

                If e.RowIndex = 36 Then
                    xHeaderText = "特殊項目a"
                ElseIf e.RowIndex = 37 Then
                    xHeaderText = "特殊項目b"
                Else
                    xHeaderText = "特殊項目c"
                End If

                ' セルの値を描画
                TextRenderer.DrawText(e.Graphics, xHeaderText, e.CellStyle.Font, xRect, e.CellStyle.ForeColor, TextFormatFlags.HorizontalCenter Or TextFormatFlags.VerticalCenter)

            End If

            ' イベント ハンドラ内で処理を行ったことを通知
            e.Handled = True

        ElseIf e.ColumnIndex = 1 Then ' 2列目の結合処理

            If e.RowIndex Mod 6 = 0 And e.RowIndex < 36 Then

                xRect = e.CellBounds
                xCell = DataGridView1(e.ColumnIndex, e.RowIndex)

                'セルの高さを足す
                xRect.Height += xCell.Size.Height * 6

                ' セルボーダーライン分矩形の位置を補正
                xRect.X -= 1
                xRect.Y -= 1

                ' 背景、セルボーダーラインを描画
                e.Graphics.FillRectangle(New SolidBrush(DataGridView1.ColumnHeadersDefaultCellStyle.BackColor), xRect)
                e.Graphics.DrawRectangle(New Pen(xDgv.GridColor), xRect)

                If e.RowIndex = 0 Then
                    xHeaderText = "中項目a"
                ElseIf e.RowIndex = 6 Then
                    xHeaderText = "中項目b"
                ElseIf e.RowIndex = 12 Then
                    xHeaderText = "中項目c"
                ElseIf e.RowIndex = 18 Then
                    xHeaderText = "中項目a"
                ElseIf e.RowIndex = 24 Then
                    xHeaderText = "中項目b"
                Else
                    xHeaderText = "中項目c"
                End If

                ' 項目名の位置を調整
                xRect.Y -= 10

                ' 項目名を描画
                TextRenderer.DrawText(e.Graphics, xHeaderText, e.CellStyle.Font, xRect, e.CellStyle.ForeColor, TextFormatFlags.HorizontalCenter Or TextFormatFlags.VerticalCenter)

            End If

            ' イベント ハンドラ内で処理を行ったことを通知
            e.Handled = True

        ElseIf e.ColumnIndex = 2 Then ' 3列目の描画処理

            If e.RowIndex < 36 Then

                xRect = e.CellBounds
                xCell = DataGridView1(e.ColumnIndex, e.RowIndex)

                ' セルボーダーライン分矩形の位置を補正
                xRect.X -= 1
                xRect.Y -= 1

                ' 背景、セルボーダーラインを描画
                e.Graphics.FillRectangle(New SolidBrush(DataGridView1.ColumnHeadersDefaultCellStyle.BackColor), xRect)
                e.Graphics.DrawRectangle(New Pen(xDgv.GridColor), xRect)

                '項目名設定
                If e.RowIndex Mod 6 = 0 Then
                    xHeaderText = "小項目a"
                ElseIf e.RowIndex Mod 6 = 1 Then
                    xHeaderText = "小項目b"
                ElseIf e.RowIndex Mod 6 = 2 Then
                    xHeaderText = "小項目c"
                ElseIf e.RowIndex Mod 6 = 3 Then
                    xHeaderText = "小項目e"
                ElseIf e.RowIndex Mod 6 = 4 Then
                    xHeaderText = "小項目f"
                ElseIf e.RowIndex Mod 6 = 5 Then
                    xHeaderText = "小項目f"
                End If

                ' セルの値を描画
                TextRenderer.DrawText(e.Graphics, xHeaderText, e.CellStyle.Font, xRect, e.CellStyle.ForeColor, TextFormatFlags.Left Or TextFormatFlags.VerticalCenter)

            End If

            ' イベント ハンドラ内で処理を行ったことを通知
            e.Handled = True

        End If

    End Sub

引用返信 編集キー/
■73187 / inTopicNo.2)  Re[1]: DataGridViewのセル結合の描画について
□投稿者/ 魔界の仮面弁士 (102回)-(2014/08/26(Tue) 18:48:06)
2014/08/27(Wed) 08:59:06 編集(投稿者)

No73186 (てくてく さん) に返信
> スクロールバーで動かすと描画が正しくできません。
上方向へのスクロールもそうですが、
左方向へのスクロールも深刻かと。


それから、今回はセルの編集は想定されていないのでしょうか?

セル内の文字列は、イベント中で行列指定で固定割当するのでは無く、
DataSource からバインドしておき、e.Value ないしは e.FormattedValue で
取り出したデータを DrawText した方が良い気がします。

EditingControl にまで手を加えるとか、そもそも ReadOnly である場合は
現状のように、CellPainting 内に文字列情報を持たせても良いですけれども。



> ひとまず自力でセルを描画する事で、上図のようなグリッドは作成できたのですが、
まずは「e.Handled = True にはするが、何も描画しないセル」があるのが問題です。
描画処理を記述しないセルは、e.Handled = False のままにしておく必要があります。

描画処理を施していないセルは、スクロール時などの「再描画」が
行われませんので、今回のような問題が生じます。


なお、スクロール時に連結した項目のテキストが隠れてしまわないよう

|−−−−|−−−−|−−−−|−|
|大項目a |中項目a |小項目b |▲|
| | |小項目c |−|
| | |小項目d | |
| | |小項目e | |

のように、スクロールに応じて描画位置を変更するのか、それとも、

|−−−−|−−−−|−−−−|−|
|    |    |小項目b |▲|
| | |小項目c |−|
| | |小項目d | |
| | |小項目e | |

のように、見切れてしまって良いのかによっても、描画方法が
異なります。前者の場合、結合セルに対する描画イベントでは、
自セルだけではなく、隣接セルの領域も含めて、
背景色やボーダーの上描きが必要となります。


それともう一点。

現在のソースだと、ColumnIndex や RowIndex が -1 であった場合に
対応できていないようです。そのためこのままでは、
 xCell = DataGridView1(e.ColumnIndex, e.RowIndex)
の処理を行うところで、インデクサが ArgumentOutOfRangeException を
発してしまうことになります。
行ヘッダ/列ヘッダを表示していないのであれば、そもそも
-1 に対するイベントは発生しませんが、一応、対処しておくべきかと。
引用返信 編集キー/
■73190 / inTopicNo.3)  Re[2]: DataGridViewのセル結合の描画について
□投稿者/ てくてく (2回)-(2014/08/27(Wed) 11:22:38)
魔界の仮面弁士 さん
早速の書込みありがとうございます。


> それから、今回はセルの編集は想定されていないのでしょうか?

> セル内の文字列は、イベント中で行列指定で固定割当するのでは無く、
> DataSource からバインドしておき、e.Value ないしは e.FormattedValue で
> 取り出したデータを DrawText した方が良い気がします。
> EditingControl にまで手を加えるとか、そもそも ReadOnly である場合は
> 現状のように、CellPainting 内に文字列情報を持たせても良いですけれども。


説明不足ですみません。

図に書いた大項目〜小項目はヘッダになります。
実際は小項目の左に編集セルが入る事になります。
よってヘッダのセルはReadOnlyにしている為、固定割当を行っています。


> まずは「e.Handled = True にはするが、何も描画しないセル」があるのが問題です。
> 描画処理を記述しないセルは、e.Handled = False のままにしておく必要があります。

「e.Handled = False のまま」という事ですが、DataGridViewに慣れていない為、どのような処理を入れればよいかがよくわからず・・・。
ひとまず以下のように修正してみたのですが、今まで正しく表示されていたグリッドが変になってしまいました・・・。


Private Sub DataGridView1_CellPainting(ByVal sender As System.Object, ByVal e As System.Windows.Forms.DataGridViewCellPaintingEventArgs) Handles DataGridView1.CellPainting

        Dim xDgv As DataGridView = DirectCast(sender, DataGridView)
        Dim xRect As Rectangle
        Dim xCell As DataGridViewCell
        Dim xHeaderText As String = String.Empty

        ' 行・列共にヘッダは処理しない
        If e.RowIndex < 0 OrElse e.ColumnIndex < 0 Then
            Return
        End If

        If e.ColumnIndex = 0 Then ' 1列目の処理

            xRect = e.CellBounds

            If e.RowIndex Mod 18 = 0 And e.RowIndex < 36 Then

                xRect = e.CellBounds
                xCell = DataGridView1(e.ColumnIndex, e.RowIndex)

                'セルの高さを足す
                xRect.Height += xCell.Size.Height * 17

                ' セルボーダーライン分矩形の位置を補正
                xRect.X -= 1
                xRect.Y -= 1

                ' 背景、セルボーダーラインを描画
                e.Graphics.FillRectangle(New SolidBrush(DataGridView1.ColumnHeadersDefaultCellStyle.BackColor), xRect)
                e.Graphics.DrawRectangle(New Pen(xDgv.GridColor), xRect)

                If e.RowIndex = 0 Then
                    xHeaderText = "大項目a"
                Else
                    xHeaderText = "大項目b"
                End If

                ' セルの値を描画
                TextRenderer.DrawText(e.Graphics, xHeaderText, e.CellStyle.Font, xRect, e.CellStyle.ForeColor, TextFormatFlags.HorizontalCenter Or TextFormatFlags.VerticalCenter)

                ' イベント ハンドラ内で処理を行ったことを通知
                e.Handled = True

            ElseIf e.RowIndex >= 36 Then

                xRect = e.CellBounds
                xCell = DataGridView1(e.ColumnIndex, e.RowIndex)

                ' セル2つ分の幅を足す
                xRect.Width += xCell.Size.Width * 2
                xRect.X -= 1
                xRect.Y -= 1

                ' 背景、セルボーダーラインを描画
                e.Graphics.FillRectangle(New SolidBrush(DataGridView1.ColumnHeadersDefaultCellStyle.BackColor), xRect)
                e.Graphics.DrawRectangle(New Pen(xDgv.GridColor), xRect)

                If e.RowIndex = 36 Then
                    xHeaderText = "特殊項目a"
                ElseIf e.RowIndex = 37 Then
                    xHeaderText = "特殊項目b"
                Else
                    xHeaderText = "特殊項目c"
                End If

                ' セルの値を描画
                TextRenderer.DrawText(e.Graphics, xHeaderText, e.CellStyle.Font, xRect, e.CellStyle.ForeColor, TextFormatFlags.HorizontalCenter Or TextFormatFlags.VerticalCenter)

                ' イベント ハンドラ内で処理を行ったことを通知
                e.Handled = True
            Else
                e.Handled = False
            End If

        ElseIf e.ColumnIndex = 1 Then ' 2列目の結合処理

            If e.RowIndex Mod 6 = 0 And e.RowIndex < 36 Then

                xRect = e.CellBounds
                xCell = DataGridView1(e.ColumnIndex, e.RowIndex)

                'セルの高さを足す
                xRect.Height += xCell.Size.Height * 6

                ' セルボーダーライン分矩形の位置を補正
                xRect.X -= 1
                xRect.Y -= 1

                ' 背景、セルボーダーラインを描画
                e.Graphics.FillRectangle(New SolidBrush(DataGridView1.ColumnHeadersDefaultCellStyle.BackColor), xRect)
                e.Graphics.DrawRectangle(New Pen(xDgv.GridColor), xRect)

                If e.RowIndex = 0 Then
                    xHeaderText = "中項目a"
                ElseIf e.RowIndex = 6 Then
                    xHeaderText = "中項目b"
                ElseIf e.RowIndex = 12 Then
                    xHeaderText = "中項目c"
                ElseIf e.RowIndex = 18 Then
                    xHeaderText = "中項目a"
                ElseIf e.RowIndex = 24 Then
                    xHeaderText = "中項目b"
                Else
                    xHeaderText = "中項目c"
                End If

                ' 項目名の位置を調整
                xRect.Y -= 10

                ' 項目名を描画
                TextRenderer.DrawText(e.Graphics, xHeaderText, e.CellStyle.Font, xRect, e.CellStyle.ForeColor, TextFormatFlags.HorizontalCenter Or TextFormatFlags.VerticalCenter)

                ' イベント ハンドラ内で処理を行ったことを通知
                e.Handled = True
            Else
                e.Handled = False
            End If

        ElseIf e.ColumnIndex = 2 Then ' 3列目の描画処理

            If e.RowIndex < 36 Then

                xRect = e.CellBounds
                xCell = DataGridView1(e.ColumnIndex, e.RowIndex)

                ' セルボーダーライン分矩形の位置を補正
                xRect.X -= 1
                xRect.Y -= 1

                ' 背景、セルボーダーラインを描画
                e.Graphics.FillRectangle(New SolidBrush(DataGridView1.ColumnHeadersDefaultCellStyle.BackColor), xRect)
                e.Graphics.DrawRectangle(New Pen(xDgv.GridColor), xRect)

                '項目名設定
                If e.RowIndex Mod 6 = 0 Then
                    xHeaderText = "小項目a"
                ElseIf e.RowIndex Mod 6 = 1 Then
                    xHeaderText = "小項目b"
                ElseIf e.RowIndex Mod 6 = 2 Then
                    xHeaderText = "小項目c"
                ElseIf e.RowIndex Mod 6 = 3 Then
                    xHeaderText = "小項目e"
                ElseIf e.RowIndex Mod 6 = 4 Then
                    xHeaderText = "小項目f"
                ElseIf e.RowIndex Mod 6 = 5 Then
                    xHeaderText = "小項目f"
                End If

                ' セルの値を描画
                TextRenderer.DrawText(e.Graphics, xHeaderText, e.CellStyle.Font, xRect, e.CellStyle.ForeColor, TextFormatFlags.Left Or TextFormatFlags.VerticalCenter)

                ' イベント ハンドラ内で処理を行ったことを通知
                e.Handled = True
            Else
                e.Handled = False
            End If
        End If
    End Sub




> なお、スクロール時に連結した項目のテキストが隠れてしまわないよう
> 
> |−−−−|−−−−|−−−−|−|
> |大項目a |中項目a |小項目b |▲|
> |	 | 	  |小項目c |−|
> |	 | 	  |小項目d | |
> |	 | 	  |小項目e | |
> 
> のように、スクロールに応じて描画位置を変更するのか、それとも、
> 
> |−−−−|−−−−|−−−−|−|
> |    |    |小項目b |▲|
> |	 | 	  |小項目c |−|
> |	 | 	  |小項目d | |
> |	 | 	  |小項目e | |
> 
> のように、見切れてしまって良いのかによっても、描画方法が
> 異なります。


後者を考えています。この場合は特に処理は不要という事になるのでしょうか?


> それともう一点。
> 
> 現在のソースだと、ColumnIndex や RowIndex が -1 であった場合に
> 対応できていないようです。そのためこのままでは、
>  xCell = DataGridView1(e.ColumnIndex, e.RowIndex)
> の処理を行うところで、インデクサが ArgumentOutOfRangeException を
> 発してしまうことになります。
> 行ヘッダ/列ヘッダを表示していないのであれば、そもそも
> -1 に対するイベントは発生しませんが、一応、対処しておくべきかと。


確かにその通りだと思います。
行ヘッダ/列ヘッダは非表示にしているのですが、
処理を入れておきます。


引用返信 編集キー/
■73191 / inTopicNo.4)  Re[3]: DataGridViewのセル結合の描画について
□投稿者/ 魔界の仮面弁士 (103回)-(2014/08/27(Wed) 12:30:30)
No73190 (てくてく さん) に返信
> ひとまず以下のように修正してみたのですが、今まで正しく表示されていたグリッドが変になってしまいました・・・。

RowIndex が 1 〜 35 のセルは、大分類の列をマージするのですよね。

現状は、1〜35 の時は e.Handled = False にしているようですが、ということは、
それらのセルは標準のセル描画に任せるのということになります。

事前に「大分類」セルのスタイルで、背景色等を調整してあるかどうか確認して下さい。

もしも、そうした標準描画に任せるのでは無く、背景や枠線などを
カスタム描画する必要があるのなら、1〜35 の行に対しても、
結合セルのために e.Paint なり、e.Graphics.FillRectangle なりを施し、
e.Handled = True のモードにしておく必要があります。
引用返信 編集キー/
■73196 / inTopicNo.5)  Re[4]: DataGridViewのセル結合の描画について
□投稿者/ てくてく (3回)-(2014/08/27(Wed) 19:11:12)
魔界の仮面弁士 さん

> もしも、そうした標準描画に任せるのでは無く、背景や枠線などを
> カスタム描画する必要があるのなら、1〜35 の行に対しても、
> 結合セルのために e.Paint なり、e.Graphics.FillRectangle なりを施し、
> e.Handled = True のモードにしておく必要があります。


RowIndex が 1 〜 35 のセル描画処理を自力で描画するようにし、
無事に想定通りの処理ができるようになりました。

いろいろとアドバイス有難うございました。
解決済み
引用返信 編集キー/


トピック内ページ移動 / << 0 >>

このトピックに書きこむ

過去ログには書き込み不可

管理者用

- Child Tree -