DataGridViewコントロールでは、同じ内容のセルが連続して並ぶ場合に、それらを1つのセルにまとめることにより表示を見やすくできる。以下の画面は通常の表示(上)と同じセルを1つにまとめた場合の表示(下)の例である。
このような表示は、不要なセルの値と境界線を消すことにより実現できる。本稿では、次のような手順によりこれを実装している。
- セルの値が1つ上のセルと同じであれば、その値を空文字に置き換える
 - すべてのセルにおいて下側の境界線を描画しない
 - セルの値が1つ上のセルと同じであれば、そのセルの上側の境界線を描画しない
 
1.のセルの値を書き換える処理は、セルが表示される直前にセルごとに発生するCellFormattingイベントのタイミングで行うことができる。また、2.と3.のセルの境界線の描画設定については、セルの描画時にセルごとに発生するCellPaintingイベントのタイミングで行える。どちらのイベントにおいても、イベント・ハンドラに渡されるパラメータから、現在処理中のセルの位置を知ることができる。
まず以下に、これらの手順を実装したサンプル・プログラムを示す。上記の画面はそのサンプル・プログラムの実行例である。
連続する同じ内容のセルを1つのセルにまとめるサンプル・プログラム
このサンプル・プログラムでは、DataGridViewコントロールに表示するデータとして、@ITの新着記事についてのRSS情報(RSS 2.0)を使用している。プログラムでは、このRSS情報から、記事の公開日、公開しているフォーラム、記事タイトルのみをグリッドに表示する。RSS情報の取得とDataGridViewコントロールの初期化は、フォームのLoadイベント・ハンドラで行っている。
// dgvgroupedcell.cs
using System;
using System.Data;
using System.Drawing;
using System.Windows.Forms;
public class MyForm : Form {
  DataGridView dgv;
  // 指定したセルと1つ上のセルの値を比較
  bool IsTheSameCellValue(int column, int row) {
    DataGridViewCell cell1 = dgv[column, row];
    DataGridViewCell cell2 = dgv[column, row - 1];
    if (cell1.Value == null || cell2.Value == null) {
      return false;
    }
    // ここでは文字列としてセルの値を比較
    if (cell1.Value.ToString() == cell2.Value.ToString()) {
      return true;
    } else {
      return false;
    }
  }
  // DataGridViewのCellFormattingイベント・ハンドラ
  void dgv_CellFormatting(object sender, DataGridViewCellFormattingEventArgs e) {
    // 1行目については何もしない
    if (e.RowIndex == 0)
      return;
    if (IsTheSameCellValue(e.ColumnIndex, e.RowIndex)) {
      e.Value = "";
      e.FormattingApplied = true; // 以降の書式設定は不要
    }
  }
  // DataGridViewのCellPaintingイベント・ハンドラ
  void dgv_CellPainting(object sender, DataGridViewCellPaintingEventArgs e) {
    // セルの下側の境界線を「境界線なし」に設定
    e.AdvancedBorderStyle.Bottom = DataGridViewAdvancedCellBorderStyle.None; (1)
    // 1行目や列ヘッダ、行ヘッダの場合は何もしない
    if (e.RowIndex < 1 || e.ColumnIndex < 0)
      return;
    if (IsTheSameCellValue(e.ColumnIndex, e.RowIndex)) {
      // セルの上側の境界線を「境界線なし」に設定
      e.AdvancedBorderStyle.Top = DataGridViewAdvancedCellBorderStyle.None; (2)
    } else {
      // セルの上側の境界線を既定の境界線に設定
      e.AdvancedBorderStyle.Top = dgv.AdvancedCellBorderStyle.Top; (3)
    }
  }
  // フォームのLoadイベント・ハンドラ
  void MyForm_Load(object sender, EventArgs e) {
    // @ITのRSS情報をデータセットに読み込む
    DataSet ds = new DataSet();
    ds.ReadXml("http://atmarkit.itmedia.co.jp/rss/rss.xml");
    // 3つの列(公開日、フォーラム、タイトル)のみを表示
    dgv.ColumnCount = 3;
    dgv.Columns[0].DataPropertyName = "pubDate";
    dgv.Columns[0].HeaderText = "公開日";
    dgv.Columns[1].DataPropertyName = "category";
    dgv.Columns[1].HeaderText = "フォーラム";
    dgv.Columns[2].DataPropertyName = "title";
    dgv.Columns[2].HeaderText = "記事タイトル";
    // 列の自動生成を行わない
    dgv.AutoGenerateColumns = false;
    // RSS内の<item>要素部分をデータソースとして使用
    dgv.DataSource = ds.Tables["item"];
  }
  // フォームのコンストラクタ
  public MyForm() {
    dgv = new DataGridView();
    dgv.Dock = DockStyle.Fill;
    dgv.CellFormatting += new DataGridViewCellFormattingEventHandler(dgv_CellFormatting);
    dgv.CellPainting += new DataGridViewCellPaintingEventHandler(dgv_CellPainting);
    this.Controls.Add(dgv);
    this.Size = new Size(480, 240);
    this.Load += new EventHandler(MyForm_Load);
  }
}
class Program {
  [STAThread]
  static void Main() {
    Application.Run(new MyForm());
  }
}
// コンパイル方法:csc dgvgroupedcell.cs
dgvgroupedcell.csのダウンロード
' dgvgroupedcell.vb
Imports System
Imports System.Data
Imports System.Drawing
Imports System.Windows.Forms
Public Class MyForm
    Inherits Form
  WithEvents dgv As DataGridView
  ' 指定したセルと1つ上のセルの値を比較
  Function IsTheSameCellValue(ByVal column As Integer, ByVal row As Integer) As Boolean
    Dim cell1 As DataGridViewCell = dgv(column, row)
    Dim cell2 As DataGridViewCell = dgv(column, row - 1)
    If cell1.Value = Nothing Or cell2.Value = Nothing Then
      Return False
    End If
    ' ここでは文字列としてセルの値を比較
    If cell1.Value.ToString() = cell2.Value.ToString() Then
      Return True
    Else
      Return False
    End If
  End Function
  ' DataGridViewのCellFormattingイベント・ハンドラ
  Sub dgv_CellFormatting(sender As Object, e As DataGridViewCellFormattingEventArgs) Handles dgv.CellFormatting
    ' 1行目については何もしない
    If e.RowIndex = 0 Then
      Return
    End If
    If IsTheSameCellValue(e.ColumnIndex, e.RowIndex) Then
      e.Value = ""
      e.FormattingApplied = true ' 以降の書式設定は不要
    End If
  End Sub
  ' DataGridViewのCellPaintingイベント・ハンドラ
  Sub dgv_CellPainting(sender As Object, e As DataGridViewCellPaintingEventArgs) Handles dgv.CellPainting
    ' セルの下側の境界線を「境界線なし」に設定
    e.AdvancedBorderStyle.Bottom = DataGridViewAdvancedCellBorderStyle.None (1)
    ' 1行目や列ヘッダ、行ヘッダの場合は何もしない
    If e.RowIndex < 1 Or e.ColumnIndex < 0 Then
      Return
    End If
    If IsTheSameCellValue(e.ColumnIndex, e.RowIndex) Then
      ' セルの上側の境界線を「境界線なし」に設定
      e.AdvancedBorderStyle.Top = DataGridViewAdvancedCellBorderStyle.None (2)
    Else
      ' セルの上側の境界線を既定の境界線に設定
      e.AdvancedBorderStyle.Top = dgv.AdvancedCellBorderStyle.Top (3)
    End If
  End Sub
  ' フォームのLoadイベント・ハンドラ
  Sub MyForm_Load(sender As Object, e As EventArgs) Handles MyBase.Load
    ' @ITのRSS情報をデータセットに読み込む
    Dim ds As New DataSet()
    ds.ReadXml("http://rss.rssad.jp/rss/itm/rss.xml")
    ' 3つの列(公開日、フォーラム、タイトル)のみを表示
    dgv.ColumnCount = 3
    dgv.Columns(0).DataPropertyName = "pubDate"
    dgv.Columns(0).HeaderText = "公開日"
    dgv.Columns(1).DataPropertyName = "category"
    dgv.Columns(1).HeaderText = "フォーラム"
    dgv.Columns(2).DataPropertyName = "title"
    dgv.Columns(2).HeaderText = "記事タイトル"
    ' 列の自動生成を行わない
    dgv.AutoGenerateColumns = False
    ' RSS内の<item>要素部分をデータソースとして使用
    dgv.DataSource = ds.Tables("item")
  End Sub
  ' フォームのコンストラクタ
  Public Sub New()
    dgv = New DataGridView()
    dgv.Dock = DockStyle.Fill
    Me.Controls.Add(dgv)
    Me.Size = new Size(480, 240)
  End Sub
End Class
' コンパイル方法:vbc /main:MyForm dgvgroupedcell.vb
dgvgroupedcell.vbのダウンロード
プログラムの最初にあるIsTheSameCellValueメソッドは、縦に並んだ2つのセルの値が同じかどうかをチェックするためのものだ。
CellFormattingイベント・ハンドラでは、イベントの発生したセルと、その1つ上のセルの値が同じであれば、セルの値を空白文字に置き換えている。このイベントについては「TIPS:DataGridViewコントロールで特定の値のセルを強調表示するには?」でも解説しているので参考にしてほしい。
セルの描画時に呼び出されるCellPaintingイベント・ハンドラでは、そのメソッドに渡されるDataGridViewCellPaintingEventArgsクラス(System.Windows.Forms名前空間)のオブジェクトのAdvancedBorderStyleプロパティにより、個々のセルにおける上下左右の境界線の描画のスタイルを指定できる。
DataGridViewコントロールのAdvancedCellBorderStyleプロパティでもセルの境界線スタイルを変更できるが、セルごとに個別の境界線スタイルを指定したい場合には、CellPaintingイベント・ハンドラを利用する必要がある。
なお、本来ならばCellPaintingイベントの発生しているセルの上側の境界線スタイルと、その1つ上のセルの下側の境界線スタイルを「境界線なし」にしたいところだが、コード中の(1)で、すべてのセルについて下側の境界線を「境界線なし」に設定しているのは、イベントの発生していないセルの境界線スタイルを変更できないためである。
また、(3)の行は本来不要なはずだが、(1)で下側の境界線を消してしまうとその1つ下のセルの上側の境界線も消えてしまうため(つまりすべての横線が消える)、CellPaintingイベントの発生しているセルと1つ上のセルと値が異なる場合には、(3)によりセルの上側の境界線が描画されるようにしている。このため実際には、(2)の行がなくても表示は変わらない。
利用可能バージョン:.NET Framework 2.0のみ
カテゴリ:Windowsフォーム 処理対象:DataGridViewコントロール
使用ライブラリ:DataGridViewコントロール
使用ライブラリ:DataGridViewCellPaintingEventArgsクラス(System.Windows.Forms名前空間)
関連TIPS:DataGridViewコントロールで特定の値のセルを強調表示するには?
Copyright© Digital Advantage Corp. All Rights Reserved.

