- PR -

DataGridViewで文字単位でフォントを変える (DataGridViewRichTextBoxColumnの実装)

投稿者投稿内容
hei
ベテラン
会議室デビュー日: 2006/09/07
投稿数: 78
投稿日時: 2007-01-26 18:59
DataGridViewの文字単位でセル単位でフォントを変えるサンプルはたくさんあるのですが、
文字単位で変えるものはさっぱり見つかりません。
どうもRichTextBoxを配置できるようにDataGridViewColumnを継承したクラスを実装しなければならないようです。
(もっと簡単な方法があれば教えて下さい)

http://msdn2.microsoft.com/ja-jp/library/7tas5c80(VS.80).aspx
を参考にしてやってみたのですが、全く描画されません。
このサンプルは、「テキストボックスの表示機能を再度実装しなくてもすむように、DataGridViewCellクラスを直接継承するのではなくDataGridViewTextBoxCellクラスから派生します」
とあるので、この部分が原因だと思うのですが、そのテキストボックスの表示機能を再度実装するにはどのようにしたらいいのでしょうか?

よろしくお願いします。

Public Class DataGridViewRichTextBoxColumn
Inherits DataGridViewColumn
Public Sub New()
MyBase.New(New DataGridViewRichTextBoxCell)
End Sub
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 value Is Nothing AndAlso Not value.GetType.IsAssignableFrom(GetType(DataGridViewRichTextBoxCell)) Then
Throw New InvalidCastException("Must be a DataGridViewRichTextBoxCell")
End If
MyBase.CellTemplate = value
End Set
End Property
End Class

Public Class DataGridViewRichTextBoxCell
Inherits DataGridViewCell

Public Overrides Sub InitializeEditingControl(ByVal rowIndex As Integer, ByVal initialFormattedValue As Object, ByVal dataGridViewCellStyle As System.Windows.Forms.DataGridViewCellStyle)
MyBase.InitializeEditingControl(rowIndex, initialFormattedValue, dataGridViewCellStyle)
Dim ctrl As RichTextBoxEditingControl = CType(DataGridView.EditingControl, RichTextBoxEditingControl)
ctrl.Text = CType(Me.Value, String)
End Sub
Public Overrides Property ValueType() As System.Type
Get
Return GetType(String)
End Get
Set(ByVal value As System.Type)
MyBase.ValueType = value
End Set
End Property
Public Overrides ReadOnly Property EditType() As System.Type
Get
Return GetType(RichTextBoxEditingControl)
End Get
End Property
End Class

Public Class RichTextBoxEditingControl
Inherits RichTextBox
Implements IDataGridViewEditingControl

Private _dataGridView As DataGridView
Private _rowIndex As Integer
Private _valueChanged As Boolean = False
Public Sub ApplyCellStyleToEditingControl(ByVal dataGridViewCellStyle As System.Windows.Forms.DataGridViewCellStyle) Implements System.Windows.Forms.IDataGridViewEditingControl.ApplyCellStyleToEditingControl
Me.Font = dataGridViewCellStyle.Font
Me.ForeColor = dataGridViewCellStyle.ForeColor
Me.BackColor = dataGridViewCellStyle.BackColor
End Sub

Public Property EditingControlDataGridView() As System.Windows.Forms.DataGridView Implements System.Windows.Forms.IDataGridViewEditingControl.EditingControlDataGridView
Get
Return _dataGridView
End Get
Set(ByVal value As System.Windows.Forms.DataGridView)
_dataGridView = value
End Set
End Property

Public Property EditingControlFormattedValue() As Object Implements System.Windows.Forms.IDataGridViewEditingControl.EditingControlFormattedValue
Get
Return Me.Text
End Get
Set(ByVal value As Object)
If TypeOf value Is [String] Then
Me.Text = value.ToString
End If
End Set
End Property

Public Property EditingControlRowIndex() As Integer Implements System.Windows.Forms.IDataGridViewEditingControl.EditingControlRowIndex
Get
Return _rowIndex
End Get
Set(ByVal value As Integer)
_rowIndex = value
End Set
End Property

Public Property EditingControlValueChanged() As Boolean Implements System.Windows.Forms.IDataGridViewEditingControl.EditingControlValueChanged
Get
Return _valueChanged
End Get
Set(ByVal value As Boolean)
_valueChanged = value
End Set
End Property

Public Function EditingControlWantsInputKey(ByVal keyData As System.Windows.Forms.Keys, ByVal dataGridViewWantsInputKey As Boolean) As Boolean Implements System.Windows.Forms.IDataGridViewEditingControl.EditingControlWantsInputKey
Return True
End Function

Public ReadOnly Property EditingPanelCursor() As System.Windows.Forms.Cursor Implements System.Windows.Forms.IDataGridViewEditingControl.EditingPanelCursor
Get
Return MyBase.Cursor
End Get
End Property

Public Function GetEditingControlFormattedValue(ByVal context As System.Windows.Forms.DataGridViewDataErrorContexts) As Object Implements System.Windows.Forms.IDataGridViewEditingControl.GetEditingControlFormattedValue
Return Me.Text
End Function

Public Sub PrepareEditingControlForEdit(ByVal selectAll As Boolean) Implements System.Windows.Forms.IDataGridViewEditingControl.PrepareEditingControlForEdit

End Sub

Public ReadOnly Property RepositionEditingControlOnValueChange() As Boolean Implements System.Windows.Forms.IDataGridViewEditingControl.RepositionEditingControlOnValueChange
Get
Return False
End Get
End Property

End Class

Public Class Form1
Private _view As New DataGridView
Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
Dim view As DataGridView = _view
Me.Controls.Add(view)
view.Dock = DockStyle.Fill

Dim col As New DataGridViewRichTextBoxColumn
view.Columns.Add(col)
view.RowCount = 5

For Each row As DataGridViewRow In view.Rows
row.Cells(0).Value = "aiueo"
Next
End Sub
End Class
KI
大ベテラン
会議室デビュー日: 2007/01/10
投稿数: 239
投稿日時: 2007-01-26 21:57
書いて頂いたコードで、編集モードのときはRichTextBoxのようなものが
表示されているようですが、編集モードでない状態のセルで、
RichTextBoxで設定された文字色等をそのまま表示したいということでしょうか?

DataGridViewTextBoxColumnには、文字ごとにフォント色を変えるような機能はありませんので、
Paintメソッドをオーバーライドして、自分で描画するくらいしか思いつきませんが…
hei
ベテラン
会議室デビュー日: 2006/09/07
投稿数: 78
投稿日時: 2007-01-26 23:45
KIさん、ありがとうございます。

>RichTextBoxで設定された文字色等をそのまま表示したいということでしょうか?

そうです。

>Paintメソッドをオーバーライドして、自分で描画するくらいしか思いつきませんが…

私もこの様なことが出来ないかと思ったのですが、
オブジェクトブラウザを見てもTextBoxにオーバーライドできるPaintメソッドはないようです
OnPaintをオーバーライドして,e.Graphics.DrawString(〜としてみても何もおきません

KI
大ベテラン
会議室デビュー日: 2007/01/10
投稿数: 239
投稿日時: 2007-01-27 01:59
頑張ってはみたのですが…あと一歩のところでわからなくなりました。
描画はDataGridViewCellのPaintメソッドのオーバーライドで行えます。
しかし、問題はEditingControlとDataGridViewCellの間で渡す値の方かも知れません。
書式情報等を含めた情報をやりとりする必要がありますので、
RichTextBoxのTextではなくRtfを渡すようにしました。
あとは、RtfをどうやってImageに変換するかというところです。
RichTextBoxからイメージを取得できれば早いと思うのですが…

少々長くなりますが、現在のコードです。

コード:
Public Class DataGridViewRichTextBoxColumn
    Inherits DataGridViewColumn

    Public Sub New()
        MyBase.New(New DataGridViewRichTextBoxCell())
    End Sub

    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 value Is Nothing AndAlso Not value.GetType.IsAssignableFrom(GetType(DataGridViewRichTextBoxCell)) Then
                Throw New InvalidCastException("Must be a DataGridViewRichTextBoxCell")
            End If
            MyBase.CellTemplate = value
        End Set
    End Property
End Class

Public Class DataGridViewRichTextBoxCell
    Inherits DataGridViewCell

    '描画処理
    Protected Overrides Sub Paint(ByVal graphics As System.Drawing.Graphics, _
        ByVal clipBounds As System.Drawing.Rectangle, _
        ByVal cellBounds As System.Drawing.Rectangle, _
        ByVal rowIndex As Integer, _
        ByVal cellState As System.Windows.Forms.DataGridViewElementStates, _
        ByVal value As Object, _
        ByVal formattedValue As Object, _
        ByVal errorText As String, _
        ByVal cellStyle As System.Windows.Forms.DataGridViewCellStyle, _
        ByVal advancedBorderStyle As System.Windows.Forms.DataGridViewAdvancedBorderStyle, _
        ByVal paintParts As System.Windows.Forms.DataGridViewPaintParts)

        '背景の描画
        If (paintParts And DataGridViewPaintParts.Background) = _
            DataGridViewPaintParts.Background Then

            Dim cellBackground As New SolidBrush(cellStyle.BackColor)
            graphics.FillRectangle(cellBackground, cellBounds)
            cellBackground.Dispose()
        End If

        '境界線の描画
        If (paintParts And DataGridViewPaintParts.Border) = _
            DataGridViewPaintParts.Border Then

            PaintBorder(graphics, clipBounds, cellBounds, cellStyle, _
                advancedBorderStyle)
        End If

        '描画エリアを計算
        Dim drawArea As Rectangle = cellBounds
        Dim drawAdjustment As Rectangle = _
            Me.BorderWidths(advancedBorderStyle)
        drawArea.X += drawAdjustment.X
        drawArea.Y += drawAdjustment.Y
        drawArea.Height -= drawAdjustment.Height
        drawArea.Width -= drawAdjustment.Width

        '文字列に変換
        Dim strValue As String = TryCast(value, String)
        If strValue Is Nothing Then
            strValue = String.Empty
        End If

        'こんな風にvalue値(Rtf)をイメージに変換できれば
        Dim img As Image = GetImageFromRtf(strValue)

        '内容を描画
        graphics.DrawImage(img, 0, 0)

    End Sub

    '編集モード開始時、EditingControlのRtfプロパティにセルのValueをコピーする
    Public Overrides Sub InitializeEditingControl(ByVal rowIndex As Integer, ByVal initialFormattedValue As Object, ByVal dataGridViewCellStyle As System.Windows.Forms.DataGridViewCellStyle)
        MyBase.InitializeEditingControl(rowIndex, initialFormattedValue, dataGridViewCellStyle)
        Dim ctrl As RichTextBoxEditingControl = CType(DataGridView.EditingControl, RichTextBoxEditingControl)
        ctrl.Rtf = CType(Me.Value, String)
    End Sub

    '編集モードが終了して、EditingControlが削除される前にRtfプロパティの値をセルのValueにコピーする
    Public Overrides Sub DetachEditingControl()
        Dim ctrl As RichTextBoxEditingControl = CType(DataGridView.EditingControl, RichTextBoxEditingControl)
        Me.Value = ctrl.Rtf
        MyBase.DetachEditingControl()
    End Sub

    Public Overrides Property ValueType() As System.Type
        Get
            Return GetType(String)
        End Get
        Set(ByVal value As System.Type)
            MyBase.ValueType = value
        End Set
    End Property

    Public Overrides ReadOnly Property EditType() As System.Type
        Get
            Return GetType(RichTextBoxEditingControl)
        End Get
    End Property

    'RTF形式の文字列からイメージを作成
    Private Function GetImageFromRtf(ByVal rtf As String) As Image
        'ここの実装が思いつきません
    End Function

End Class

Public Class RichTextBoxEditingControl
    Inherits RichTextBox
    Implements IDataGridViewEditingControl

    Private _dataGridView As DataGridView
    Private _rowIndex As Integer
    Private _valueChanged As Boolean = False
    Public Sub ApplyCellStyleToEditingControl(ByVal dataGridViewCellStyle As System.Windows.Forms.DataGridViewCellStyle) Implements System.Windows.Forms.IDataGridViewEditingControl.ApplyCellStyleToEditingControl
        Me.Font = dataGridViewCellStyle.Font
        Me.ForeColor = dataGridViewCellStyle.ForeColor
        Me.BackColor = dataGridViewCellStyle.BackColor
    End Sub

    Public Property EditingControlDataGridView() As System.Windows.Forms.DataGridView Implements System.Windows.Forms.IDataGridViewEditingControl.EditingControlDataGridView
        Get
            Return _dataGridView
        End Get
        Set(ByVal value As System.Windows.Forms.DataGridView)
            _dataGridView = value
        End Set
    End Property

    'TextではなくRtfをValueとして扱う
    Public Property EditingControlFormattedValue() As Object Implements System.Windows.Forms.IDataGridViewEditingControl.EditingControlFormattedValue
        Get
            Return Me.Rtf
        End Get
        Set(ByVal value As Object)
            If TypeOf value Is [String] Then
                Me.Rtf = value.ToString
            End If
        End Set
    End Property

    Public Property EditingControlRowIndex() As Integer Implements System.Windows.Forms.IDataGridViewEditingControl.EditingControlRowIndex
        Get
            Return _rowIndex
        End Get
        Set(ByVal value As Integer)
            _rowIndex = value
        End Set
    End Property

    Public Property EditingControlValueChanged() As Boolean Implements System.Windows.Forms.IDataGridViewEditingControl.EditingControlValueChanged
        Get
            Return _valueChanged
        End Get
        Set(ByVal value As Boolean)
            _valueChanged = value
        End Set
    End Property

    Public Function EditingControlWantsInputKey(ByVal keyData As System.Windows.Forms.Keys, ByVal dataGridViewWantsInputKey As Boolean) As Boolean Implements System.Windows.Forms.IDataGridViewEditingControl.EditingControlWantsInputKey
        Return True
    End Function

    Public ReadOnly Property EditingPanelCursor() As System.Windows.Forms.Cursor Implements System.Windows.Forms.IDataGridViewEditingControl.EditingPanelCursor
        Get
            Return MyBase.Cursor
        End Get
    End Property

    Public Function GetEditingControlFormattedValue(ByVal context As System.Windows.Forms.DataGridViewDataErrorContexts) As Object Implements System.Windows.Forms.IDataGridViewEditingControl.GetEditingControlFormattedValue
        Return Me.EditingControlFormattedValue
    End Function

    Public Sub PrepareEditingControlForEdit(ByVal selectAll As Boolean) Implements System.Windows.Forms.IDataGridViewEditingControl.PrepareEditingControlForEdit

    End Sub

    Public ReadOnly Property RepositionEditingControlOnValueChange() As Boolean Implements System.Windows.Forms.IDataGridViewEditingControl.RepositionEditingControlOnValueChange
        Get
            Return False
        End Get
    End Property

End Class



また何か思いついたら書き込みたいと思います。
hei
ベテラン
会議室デビュー日: 2006/09/07
投稿数: 78
投稿日時: 2007-01-27 15:39
KIさん、ありがとうございます。
描画の方法など、とても参考になりました。

個人的に、標準でプロパティが用意されているべき機能だと思うのですが、
相当難しそうですね。
今回の件だけでなく、このコントロールには悩まされることも多く、
これを使わずに済む別の方法を考えてみます。
ありがとうございました!

[ メッセージ編集済み 編集者: hei 編集日時 2007-01-27 16:21 ]
KI
大ベテラン
会議室デビュー日: 2007/01/10
投稿数: 239
投稿日時: 2007-01-27 16:53
引用:

個人的に、標準でプロパティが用意されているべき機能だと思うのですが、
相当難しそうですね。



実際に書式付きのデータをDataGridViewで編集・表示するような局面が
現実問題として多いとは私には思えません。
もしかして、書式の編集をDataGridView内で行わず、
単純に文字色を部分的に変えたいだけだったのでしょうか?
それでしたら、もっと単純にCellのPaintメソッドをオーバーライドするだけで
出来るかもしれませんね。

引用:

今回の件だけでなく、このコントロールには悩まされることも多く、
これを使わずに済む別の方法を考えてみます。



同感です。
今回このコントロールをメインに使ったシステムの仕事をしたのですが、
いろいろと動作制御で悩まされたりすることが多かったです。
便利な機能は多そうですし、使いこなせればいいとは思っているのですが…
たぶんまだ勉強不足なんでしょうね。
hei
ベテラン
会議室デビュー日: 2006/09/07
投稿数: 78
投稿日時: 2007-01-28 00:23
引用:

もしかして、書式の編集をDataGridView内で行わず、
単純に文字色を部分的に変えたいだけだったのでしょうか?
それでしたら、もっと単純にCellのPaintメソッドをオーバーライドするだけで
出来るかもしれませんね。


行ごとに正規表現にマッチした文字を強調する、ということがしたいのですが、
KIさんのコードを参考にして、少し出来ました。

Public Class CustomColumn
Inherits DataGridViewColumn

Public Sub New()
MyBase.New(New CustomCell())
End Sub

Public Overrides Property CellTemplate() As DataGridViewCell
Get
Return MyBase.CellTemplate
End Get
Set(ByVal value As DataGridViewCell)
If Not (value Is Nothing) AndAlso _
Not value.GetType().IsAssignableFrom(GetType(CustomCell)) _
Then
Throw New InvalidCastException("Must be a CustomCell")
End If
MyBase.CellTemplate = value
End Set
End Property
End Class

Public Class CustomCell
Inherits DataGridViewTextBoxCell

Protected Overrides Sub Paint(ByVal graphics As System.Drawing.Graphics, ByVal clipBounds As System.Drawing.Rectangle, ByVal cellBounds As System.Drawing.Rectangle, ByVal rowIndex As Integer, ByVal cellState As System.Windows.Forms.DataGridViewElementStates, ByVal value As Object, ByVal formattedValue As Object, ByVal errorText As String, ByVal cellStyle As System.Windows.Forms.DataGridViewCellStyle, ByVal advancedBorderStyle As System.Windows.Forms.DataGridViewAdvancedBorderStyle, ByVal paintParts As System.Windows.Forms.DataGridViewPaintParts)
MyBase.Paint(graphics, clipBounds, cellBounds, rowIndex, cellState, value, formattedValue, errorText, cellStyle, advancedBorderStyle, paintParts)
'背景の描画
If paintParts And DataGridViewPaintParts.Background = DataGridViewPaintParts.Background Then
Using cellBackGround As New SolidBrush(cellStyle.BackColor)
graphics.FillRectangle(cellBackGround, cellBounds)
End Using
End If
'境界線の描画
If paintParts And DataGridViewPaintParts.Border = DataGridViewPaintParts.Border Then
PaintBorder(graphics, clipBounds, cellBounds, cellStyle, advancedBorderStyle)
End If
'描画エリアを計算

Dim drawArea As Rectangle = cellBounds
Dim drawAdjustment As Rectangle = Me.BorderWidths(advancedBorderStyle)
drawArea.X += drawAdjustment.X
drawArea.Y += drawAdjustment.Y
drawArea.Height -= drawAdjustment.Height
drawArea.Width -= drawAdjustment.Width

Dim strValue As String = value.ToString
Dim font As New Font("MS UI Gothic", 15, FontStyle.Bold Or FontStyle.Italic)
graphics.DrawString(strValue, font, Brushes.Red, drawArea.X, drawArea.Y)
End Sub

Public Overrides ReadOnly Property EditType() As Type
Get
Return GetType(CustomCell)
End Get
End Property

Public Overrides ReadOnly Property ValueType() As Type
Get
Return GetType(String)
End Get
End Property

Public Overrides ReadOnly Property DefaultNewRowValue() As Object
Get
Return Nothing
End Get
End Property

End Class


Public Class Form1
Private _view As New DataGridView
Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
Dim view As DataGridView = _view
Me.Controls.Add(view)
view.Dock = DockStyle.Fill

Dim col As New CustomColumn
view.Columns.Add(col)
view.RowCount = 5

For Each row As DataGridViewRow In view.Rows
row.Cells(0).Value = "aiueo"
Next
End Sub
End Class

このようにすることで文字の色を制御できましたが、
部分的に変える場合は、フォントなどが変わるたびに
座標を計算しなおしてそれぞれのFontやBrushを渡して
graphics.DrawStringメソッドを呼ばなければならない、と思いました。
RichTextBoxなら、文字位置を特定してフォントを指定すれば後はRichTextBoxの内部で座標の計算などをやってくれますが、
この処理を自分で実装する、ということでしょうか?
私の力では改行の位置などを求めるのは非常に難しいと思ったのですが、
もっと簡単に出来る方法はありますか?

あと、このコードでは、セルを編集した時にエラーが発生します。
単にDataGridViewTextBoxCellを継承しているので、InitializeEditingControlをオーバーライドする必要はないと思うのですが・・・

もし私の手におえないようなら、別な方法を考えなければなりませんが、
できるならやりたいと思います。
諦めるかもしれないのでざっとでかまいません。
お手数をおかけしますが、ご教授よろしくお願いします。
KI
大ベテラン
会議室デビュー日: 2007/01/10
投稿数: 239
投稿日時: 2007-01-28 01:26
コード:
If paintParts And DataGridViewPaintParts.Background = DataGridViewPaintParts.Background Then
If paintParts And DataGridViewPaintParts.Border = DataGridViewPaintParts.Border Then


この2行、こちらではエラーになりました。(Option Strictのせいだと思いますが)
私が提示したコードでは括弧がついていたと思います。

引用:

部分的に変える場合は、フォントなどが変わるたびに
座標を計算しなおしてそれぞれのFontやBrushを渡して
graphics.DrawStringメソッドを呼ばなければならない、と思いました。
RichTextBoxなら、文字位置を特定してフォントを指定すれば後はRichTextBoxの内部で座標の計算などをやってくれますが、
この処理を自分で実装する、ということでしょうか?
私の力では改行の位置などを求めるのは非常に難しいと思ったのですが、
もっと簡単に出来る方法はありますか?


おそらくそれしかないと思います。
改行が含まれるならSplitとかで区切って、
文字列の描画位置はMeasureStringで計算するしか思いつきません。
面倒ですが出来ないこともないような気はします。

引用:

あと、このコードでは、セルを編集した時にエラーが発生します。
単にDataGridViewTextBoxCellを継承しているので、InitializeEditingControlをオーバーライドする必要はないと思うのですが・・・


InitializeEditingControlとかでなく、EditTypeプロパティの実装に問題があります。
EditTypeはEditingControlの型を返さなくてはなりませんが、
DataGridViewCellの型を返していますよね。

スキルアップ/キャリアアップ(JOB@IT)