DOBON.NET DOBON.NETプログラミング掲示板過去ログ

DataGridViewに全角文字で長文を改行ありで入力するとフリーズする

環境/言語:[OS : Windows XP Professional / 言語 : C# / .NET Framework : 3.5]
分類:[.NET]

【解決したい問題】

DataGridViewの1セルに5000文字以上の文字を入力する必要があるプログラムを作成しています。

1セルに数千の文字(全角)を入力した場合、入力後再度入力したセルをクリックした際になど、CPUの使用率が100%になり、フリーズしてしまいます。

再現方法としては、DataGridViewをFormに配置し、TextBoxのカラムを1列追加します。
DataGridViewのプロパティの設定は、DataGridViewCellStyleのWrapModeをTrueに設定しているだけです。

実行後、TextBoxのセルに全角文字でデフォルトの最大文字数を入力します。
入力後、enterを押下した場合や他のセルを選択後、入力したセルをクリックした場合に、CPU使用率が100%になったまま、フリーズしてしまいます。
最大文字数の32767文字以下でもCPUが100%の状態になっている時間の程度は異なりますが、発生します。
また、該当セルの行の高さを高くし、より多くの文字列が表示されるようにすると発生しやすくなります。

半角文字の場合だと発生しないようです。
また、WrapModeをFalseに設定した場合も発生しないようです。

【解決するために何をしたか】

多くのサイトを調べましたが、同様の現象に悩む記事がありませんでした。
2009/03/21(Sat) 22:16:02 編集(投稿者)

原因は私にも分かりかねますが、
私のPC(CeleronM1.5Ghz)でも同じ現象を確認しました。
 
そこで1つの回避策として、
Textの編集と表示の最小限の機能のみを持つCellを自作したところ、
かなり動作が軽くなりました。
 
これが正しい書き方なのかは分かりませんし、
何か実装漏れがあるかもしれませんが、
私のPCではTextの編集、設定、表示が正しく動作しています。
 
以下のコードで試してみてください。
 
''' <summary>テスト用Form</summary>
Public Class DataGridViewTextBoxLongStringTestForm
    Inherits Form
 
    ' DataGridView
    Dim WithEvents _dgv As New DataGridView
    ' デバッグプリント用Button
    Dim WithEvents _btnDebug As New Button With {.Location = New Point(180, 200), .Width = 100, .Text = "DebugPrint"}
 
    Protected Overrides Sub OnLoad(ByVal e As System.EventArgs)
        ' DataGridView初期化
        With Me._dgv
            .Dock = DockStyle.Fill
            Dim col As New MyTextBoxColumn
            col.Name = "Text列"
            col.DefaultCellStyle.WrapMode = DataGridViewTriState.True
 
            .Columns.Add(col)
            .RowCount = 10
            ' セルにサンプルTextを代入
            For Each dr As DataGridViewRow In .Rows
                dr.Cells(0).Value = dr.Index.ToString + " - " + New String("あ"c, 10000)
            Next
        End With
 
        Me.Controls.Add(Me._btnDebug)
        Me.Controls.Add(Me._dgv)
 
        MyBase.OnLoad(e)
    End Sub
 
    Private Sub _dgv_EditingControlShowing(ByVal sender As Object, _
        ByVal e As System.Windows.Forms.DataGridViewEditingControlShowingEventArgs) Handles _dgv.EditingControlShowing
        Console.WriteLine("EditingControlShowing")
    End Sub
 
    ''' <summary>デバッグプリント:各セルの先頭10文字を表示</summary>
    Private Sub _btn_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles _btnDebug.Click
        For i As Integer = 0 To Me._dgv.Rows.Count - 1
            Dim ss As String = Convert.ToString(Me._dgv.Rows(i).Cells(0).Value)
            Console.WriteLine(ss.Substring(0, Math.Min(10, ss.Length)))
        Next
    End Sub
 
End Class
 
 
''' <summary>自作TextBoxColumn</summary>
Public Class MyTextBoxColumn
    Inherits DataGridViewColumn
 
    Public Overrides Property CellTemplate() As System.Windows.Forms.DataGridViewCell
        Get
            Return New MyTextBoxCell
        End Get
        Set(ByVal value As System.Windows.Forms.DataGridViewCell)
            MyBase.CellTemplate = value
        End Set
    End Property
 
End Class
 
 
''' <summary>自作TextBoxColumnのCell</summary>
Public Class MyTextBoxCell
    Inherits DataGridViewCell
 
    ''' <summary>Cellの内容を画面に描画する</summary>
    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)
 
        Dim foreBrush, backBrush As Brush
        If Me.Selected Then
            foreBrush = SystemBrushes.HighlightText
            backBrush = SystemBrushes.Highlight
        Else
            foreBrush = SystemBrushes.WindowText
            backBrush = SystemBrushes.Window
        End If
 
        graphics.FillRectangle(backBrush, cellBounds)
        Using p As New Pen(Color.LightGray)
            graphics.DrawRectangle(p, cellBounds)
        End Using
        graphics.DrawString(Convert.ToString(Me.Value), Me.DataGridView.Font, foreBrush, cellBounds)
 
    End Sub
 
    Public Overrides ReadOnly Property FormattedValueType() As System.Type
        Get
            Return GetType(String)
        End Get
    End Property
 
    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
 
    ''' <summary>編集コントロールは、既存のものをそのまま利用する</summary>
    Public Overrides ReadOnly Property EditType() As System.Type
        Get
            Return GetType(DataGridViewTextBoxEditingControl)
        End Get
    End Property
 
    ''' <summary>ダブルクリックで編集モードに入る</summary>
    Protected Overrides Sub OnDoubleClick(ByVal e As System.Windows.Forms.DataGridViewCellEventArgs)
        Me.DataGridView.BeginEdit(True)
        MyBase.OnDoubleClick(e)
    End Sub
 
    ''' <summary>編集モードに入る時に、TextBoxにCellの内容をコピーする</summary>
    Public Overrides Sub InitializeEditingControl(ByVal rowIndex As Integer, ByVal initialFormattedValue As Object, _
                                                  ByVal dataGridViewCellStyle As System.Windows.Forms.DataGridViewCellStyle)
 
        Dim tbx As TextBox = DirectCast(Me.DataGridView.EditingControl, TextBox)
        tbx.Multiline = True
        tbx.ScrollBars = ScrollBars.Vertical
        tbx.Size = Me.Size
        tbx.Text = Convert.ToString(Me.Value)
 
        MyBase.InitializeEditingControl(rowIndex, initialFormattedValue, dataGridViewCellStyle)
    End Sub
 
End Class
H.K.R.さんありがとうございます。
確かに 発生しなくなりますね。

実装内容をいろいろいじったことで、
原因が解りました。

現象はクリックした際に発生しているのではなく、
マウスがセルの範囲内に入った場合に発生するようです。

もしや…と思い、
protected override void OnCellValueChanged(DataGridViewCellEventArgs e)
{
base.OnCellValueChanged(e);

DataGridViewCell cell = this[e.ColumnIndex, e.RowIndex];

if (cell is DataGridViewTextBoxCell)
{
if (cell.Value != null)
{
cell.ToolTipText = cell.Value.ToString();
}
else
{
cell.ToolTipText = string.Empty;
}
}
}
を入れて見ました。
現象が発生するタイミングから、
ToolTipTextを作成することに時間がかかっているのでは?
と思ったので。

思ったとおり発生しなくなりました。

とりあえず現象が回避できるようになりましたので、
解決済みとさせて頂きます。
ありがとうございました。
解決済み!

DOBON.NET | プログラミング道 | プログラミング掲示板