2007年8月18日土曜日

DataGridViewのセルを拡張する(1) グルーピングセルで練習

DataGridViewの拡張を行う練習みたいなものとしてグルーピングっぽい表示を行うようなものをやってみようかと。
ちなみに元ネタはこちら。超有名どころなので大体の人は見たことがあるでしょ。

DOBON.NET
DataGridViewの行をグループ化する: .NET Tips: C#, VB.NET, Visual Studio

これは「直前の行の値と同一の値なら表示しない」という練習にはもってこいのシンプルな機能。
なので順を追ってやってみることにする。今回の場合、調べたり考えたりしなければならないのは一箇所。

  • 「直前の行の値と同一なら表示しない」というのをどこで処理すればよいか?

DataGridView関係で勘違いしやすいのは、「セル値(Valueプロパティ)が表示されている」と思いやすいこと。
実際には「セル値の変換結果が表示されている可能性が高い」くらいだと思っていいかも。可能性が高い、と妙な言い方を
したのにも理由があって、やりようによってはセルの値に関係ないものを表示することができるからなんだよね。

もう一つポイントがあって、DataGridViewのセルを拡張するときには基本として3つのクラスがセットになっているのよ。

  • DataGridViewCellから派生したセルクラス
  • DataGridViewColumnから派生したカラムクラス
  • IDataGridViewEditingControlを継承した編集コントロールクラス

基本この3つ。今回のように表示のみ行うケースはセルとカラムの2クラスでOK。
そして大体の場合、何らかの処理を行うのは「セル」か「編集コントロール」かの2択だと思っていいかな。
名前からして編集コントロール側では「編集時の動作に関連する処理」を行い、セル側では「表示時の動作に関連する処理」を行うんだよね。
なので今回はセルクラスで「表示しない」というロジックを書くことになるねぇ。そこでDataGridViewCellについてMSDNを眺めてみると
こんな記述が。

DataGridViewCell.GetFormattedValue メソッド (System.Windows.Forms)
表示用に書式指定済みのセル値を取得します

日本語の表現としてどうよ、ってのはあるけれども意味合い的には一番近いね。実際にはセルに何らかの表示を行う際には
このメソッドで処理します。なので今回もこのメソッドで処理しましょう。

''' <summary>表示用に書式指定済みのセル値を取得します。</summary>
Protected Overrides Function GetFormattedValue( _
    ByVal value As Object, _
    ByVal rowIndex As Integer, _
    ByRef cellStyle As System.Windows.Forms.DataGridViewCellStyle, _
    ByVal valueTypeConverter As System.ComponentModel.TypeConverter, _
    ByVal formattedValueTypeConverter As System.ComponentModel.TypeConverter, _
    ByVal context As System.Windows.Forms.DataGridViewDataErrorContexts) As Object

    If (rowIndex > 0) AndAlso (rowIndex <> Me.DataGridView.NewRowIndex) Then
        Dim previousCell As System.Windows.Forms.DataGridViewCell _
            = TryCast(Me.DataGridView.Rows(rowIndex - 1).Cells(Me.ColumnIndex), System.Windows.Forms.DataGridViewCell)
        If (previousCell IsNot Nothing) AndAlso (previousCell.Visible) AndAlso _
           (value IsNot Nothing) AndAlso (previousCell.Value IsNot Nothing) AndAlso _
           (value.Equals(previousCell.Value)) Then
            '直前の行と同一値ならばセルに文字を表示しない
            Return ""
        End If
    End If
    Return MyBase.GetFormattedValue(value, rowIndex, cellStyle, valueTypeConverter, formattedValueTypeConverter, context)

End Function

今回はシンプルな処理なので特に説明もいらなさそう・・・。やっていることとしては、「新規の行でない」「先頭行でない」場合に、
「一つ上の行の値」を比較して同一だったら空文字列を返却する。それだけなんだよねぇ。
最終的なソースはこんな感じ。

''' <summary>グループ表示を行うDataGridViewのセルのクラスです</summary>
''' <remarks>直前の行(一つ上の行)の値と比較し、同一の際は文字を表示しないセルです。</remarks>
Public Class GroupTextCell
    Inherits System.Windows.Forms.DataGridViewTextBoxCell

    ''' <summary>コンストラクタ</summary>
    ''' <remarks>GroupTextCellのコンストラクタを実行します</remarks>
    Public Sub New()

    End Sub

    ''' <summary>ReadOnlyプロパティ</summary>
    ''' <remarks></remarks><exclude />
    Public Overrides Property [ReadOnly]() As Boolean
        Get
            Return False
        End Get
        Set(ByVal value As Boolean)

        End Set
    End Property

    ''' <summary>表示用に書式指定済みのセル値を取得します</summary>
    Protected Overrides Function GetFormattedValue( _
        ByVal value As Object, _
        ByVal rowIndex As Integer, _
        ByRef cellStyle As System.Windows.Forms.DataGridViewCellStyle, _
        ByVal valueTypeConverter As System.ComponentModel.TypeConverter, _
        ByVal formattedValueTypeConverter As System.ComponentModel.TypeConverter, _
        ByVal context As System.Windows.Forms.DataGridViewDataErrorContexts) As Object

        If (rowIndex > 0) AndAlso (rowIndex <> Me.DataGridView.NewRowIndex) Then
            Dim previousCell As System.Windows.Forms.DataGridViewCell _
                = TryCast(Me.DataGridView.Rows(rowIndex - 1).Cells(Me.ColumnIndex), System.Windows.Forms.DataGridViewCell)
            If (previousCell IsNot Nothing) AndAlso (previousCell.Visible) AndAlso _
               (value IsNot Nothing) AndAlso (previousCell.Value IsNot Nothing) AndAlso _
               (value.Equals(previousCell.Value)) Then
                '直前の行と同一値ならばセルに文字を表示しない
                Return ""
            End If
        End If
        Return MyBase.GetFormattedValue(value, rowIndex, cellStyle, valueTypeConverter, formattedValueTypeConverter, context)
    End Function

End Class

''' <summary>グループ表示を行うDataGridViewのカラムのクラスです</summary>
''' <remarks>直前の行(一つ上の行)の値と比較し、同一の際は文字を表示しないセルを利用する際に指定するカラムです。</remarks>
Public Class GroupTextColumn
    Inherits System.Windows.Forms.DataGridViewColumn

    ''' <summary>コンストラクタ</summary>
    ''' <remarks>GroupTextColumnのコンストラクタを実行します</remarks>
    Public Sub New()
        MyBase.New(New GroupTextCell)
    End Sub

    ''' <summary>CellTemplateプロパティ</summary>
    ''' <exception cref="ArgumentException">GroupTextCell以外が指定されました</exception>
    ''' <remarks>GroupTextCell以外を指定した際には
    ''' ArgumentException例外が発生します</remarks>
    Public Overrides Property CellTemplate() As System.Windows.Forms.DataGridViewCell
        Get
            Return MyBase.CellTemplate
        End Get
        Set(ByVal value As System.Windows.Forms.DataGridViewCell)
            If Not (TypeOf value Is GroupTextCell) Then
                Throw New ArgumentException("CellTemplateプロパティはGroupTextCellを設定する必要があります。")
            End If
            MyBase.CellTemplate = value
        End Set
    End Property

End Class

練習には丁度いい感じだな。

0 件のコメント:

コメントを投稿