none
DataGridViewのテキストボックスでの和暦表示について RRS feed

  • 質問

  • おはようございます。

    http://social.msdn.microsoft.com/Forums/ja-JP/0f1b2a50-42f9-42eb-8564-213fef0dea72/datagridviewcelltemplate?forum=vbexpressja

    にて、DataGridViewのテキストボックスでの和暦表示をご教授いただきました。

    今回、使用していて気付いたのですが、和暦テキストボックスがDataGridViewの最初の列になる場合に
    エラーとなってしまいます。

    エラーの内容は下記の通りです。

    System.ArgumentOutOfRangeException:指定された引数は、有効な値の範囲内にありません。
    パラメーター名:rowIndex

    デバッグで追っていくと、
    clsDGVWarekiTextBoxCell.ToYYYYMMDD
    でエラーが発生します。

    どうしてもエラーの理由がわからないので、和暦表示のテキストボックスの前に列を作り、幅を最小にして
    対応しています。(非表示にしてもエラーになってしまうため。)

    どうか、アドバイスお願いします。

    2014年9月4日 0:30

回答

  • 実際にコンパイル可能でそのエラーが出るからプログラミングって難しいですよねー。ValueはMe.Valueって書くとわかりやすいですね。

    SurferOnWwwさんも指摘されていますが、dtを無視してなぜかMe.Valueを見に行っているのが原因です。

    Paintはヘッダも含めてすべてのセルで呼び出されますが、Paint引数で渡されるrowIndexと、自分のインスタンスのプロパティのRowIndexが一致するとは限らないようです。つまり、このときrowIndexは1とかが渡されてきても、Paintを実行しているのはRowIndex=-1である可能性がある。そのとき、ToYYYYMMDDでMe.Valueを参照したら当然エラーになります。

    • 回答としてマーク TI-cb400 2014年9月4日 7:11
    2014年9月4日 3:21
  • ToYYYYMMDDのバグです。
    DataGridViewのSelectionModeによってエラーになったりならなかったりするようで、初期状態だとエラーにならなかったようです。
    あと、他の所にもSelectionModeの影響があったので修正です。

    #Me.Valueを参照してしまっているのは別関数に切り出した時に変更し忘れたのだと思う。

    Imports System.Globalization
    Imports System.Windows.Forms
    Public Class Form1
        Inherits Form
        Public Sub New()
            Me.InitializeComponent()
    
            Dim dt As New DataTable
            dt.Columns.Add("Column1", GetType(DateTime))
            dt.Rows.Add(DateTime.Now())
    
            Dim grid As New DataGridView()
            Dim dgvclm As New DataGridViewTextBoxColumn()
            dgvclm.DataPropertyName = "Column1"
            dgvclm.CellTemplate = New DGVWarekiTextBoxCell()
            grid.Columns.Add(dgvclm)
            grid.DataSource = dt
            grid.Dock = DockStyle.Fill
            Me.Controls.Add(grid)
            dgvclm.SortMode = DataGridViewColumnSortMode.NotSortable
            grid.SelectionMode = DataGridViewSelectionMode.FullColumnSelect
            'grid.SelectionMode = DataGridViewSelectionMode.FullRowSelect
    
        End Sub
    
        Friend Class DGVWarekiTextBoxCell
            Inherits DataGridViewTextBoxCell
    
            Shared Sub New()
                jpcul = New CultureInfo("ja-jp", True)
                jpcul.DateTimeFormat.Calendar = New JapaneseCalendar()
            End Sub
    
            Private Shared jpcul As CultureInfo
            Protected Overrides Function GetFormattedValue(ByVal value As Object _
                                                           , ByVal rowIndex As Integer _
                                                           , ByRef cellStyle As DataGridViewCellStyle _
                                                           , ByVal valueTypeConverter As System.ComponentModel.TypeConverter _
                                                           , ByVal formattedValueTypeConverter As System.ComponentModel.TypeConverter _
                                                           , ByVal context As DataGridViewDataErrorContexts) As Object
                If value Is Nothing OrElse value Is DBNull.Value Then
                    Return String.Empty
                End If
                Dim dt As DateTime = CType(value, DateTime)
                Try
                    Return dt.ToString("ggyy年MM月dd日", jpcul)
                Catch
                    Me.ErrorText = "日付が和暦に変換できません"
                    Return dt.ToLongDateString()
                End Try
            End Function
    
            Public Overrides Function ParseFormattedValue(ByVal formattedValue As Object _
                                                          , ByVal cellStyle As DataGridViewCellStyle _
                                                          , ByVal formattedValueTypeConverter As System.ComponentModel.TypeConverter _
                                                          , ByVal valueTypeConverter As System.ComponentModel.TypeConverter) As Object
                Dim ret As Object
                Me.ErrorText = String.Empty
    
                '編集で入力された文字列をDateTimeに変換
                Dim dt As DateTime
                Dim str As String = CType(formattedValue, String).Trim()
                If (String.IsNullOrEmpty(str)) Then
                    ret = DBNull.Value
                ElseIf ((Char.IsNumber(str.Chars(0)) AndAlso DateTime.TryParseExact(str, "yyyy/M/d", DateTimeFormatInfo.CurrentInfo, DateTimeStyles.AllowWhiteSpaces, dt)) _
                    OrElse DateTime.TryParse(str, jpcul, DateTimeStyles.AllowWhiteSpaces, dt)) Then
                    ret = dt
                Else
                    Const errmsg As String = "日付形式で入力をして下さい" & vbCrLf & "2009/1/1 又はH21/1/1"
                    'Dim ex As New ApplicationException(errmsg)
                    'MyBase.RaiseDataError(New DataGridViewDataErrorEventArgs(ex, Me.ColumnIndex, Me.RowIndex, DataGridViewDataErrorContexts.Parsing))
                    Me.ErrorText = errmsg
    
                    '@2014/9/4
                    'SelectionModeがFullRowSelect,FullColumnSelectの場合に
                    '起動直後にCells(0,0)に日付に変換できない文字を入れるとRowIndexに-1が入った状態になっている
                    'その状態でGetValueを行うとエラーになる
                    'ret = MyBase.GetValue(RowIndex)
                    If (RowIndex >= 0) Then
                        ret = MyBase.GetValue(RowIndex)
                    Else
                        ret = DBNull.Value
                    End If
                End If
    
                Return ret
            End Function
    
            Public Overrides Sub InitializeEditingControl(ByVal rowIndex As Integer _
                                                          , ByVal initialFormattedValue As Object _
                                                          , ByVal dataGridViewCellStyle As DataGridViewCellStyle)
                '編集する時は元データを西暦で表示
                '2014/9/4
                'ここでRowIndexが-1はありえないと思うけど念のため
                If (rowIndex >= 0) Then
                    Dim value As Object = MyBase.GetValue(rowIndex)
                    If value Is Nothing OrElse value Is DBNull.Value Then
                        value = String.Empty
                    Else
                        value = ToYYYYMMDD(CType(value, DateTime))
                    End If
                End If
                MyBase.InitializeEditingControl(rowIndex, Value, dataGridViewCellStyle)
            End Sub
    
            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 DataGridViewElementStates, ByVal value As Object _
                                          , ByVal formattedValue As Object, ByVal errorText As String _
                                          , ByVal cellStyle As DataGridViewCellStyle, ByVal advancedBorderStyle As DataGridViewAdvancedBorderStyle _
                                          , ByVal paintParts As DataGridViewPaintParts)
                If ((cellState And DataGridViewElementStates.Selected) = DataGridViewElementStates.Selected) _
                AndAlso value IsNot Nothing AndAlso value IsNot DBNull.Value Then
                    'AndAlso rowIndex >= 0 Then
                    '選択されているセルは西暦表示させる
    
                    formattedValue = ToYYYYMMDD(CType(value, DateTime))
                End If
                MyBase.Paint(graphics, clipBounds, cellBounds, rowIndex, cellState, value, formattedValue, errorText, cellStyle, advancedBorderStyle, paintParts)
            End Sub
    
            Private Function ToYYYYMMDD(ByVal dt As DateTime) As String
                '@2014/9/4
                'dtを使わずにMe.Valueを間違えて使ってたバグ
                'SelectionModeがFullRowSelect,FullColumnSelectの場合にMe.RowIndex=1になっていることがあって、Me.Valueがないことがある
                'Return CType(Value, DateTime).ToString("yyyy/MM/dd")
                Return CType(dt, DateTime).ToString("yyyy/MM/dd")
            End Function
        End Class
    End Class


    個別に明示されていない限りgekkaがフォーラムに投稿したコードにはフォーラム使用条件に基づき「MICROSOFT LIMITED PUBLIC LICENSE」が適用されます。(かなり自由に使ってOK!)

    • 回答としてマーク TI-cb400 2014年9月4日 7:11
    2014年9月4日 4:07

すべての返信

  • 前のスレッドを見ましたけど、結局最終的にどうしたのか分かりません。

    clsDGVWarekiTextBoxCell.ToYYYYMMDD というのも意味不明です。その前のスレッド、その前の前のスレッドと、ずっと遡って行けば分かるかもしれませんが、それを回答者に求めるのは間違ってます。

    今のコードがどうなっているのか、書いてください。

    【追伸】

    それから、質問者さんの開発環境も書いてください。(前のスレッドを見れば分かるとは言わないでくださいね)

    • 編集済み SurferOnWww 2014年9月4日 1:29 追伸追記
    2014年9月4日 1:14
  • ご回答有難うございます。

    前回の質問のスレッド自体も間違えておりました。

    下記のコードを利用しております。

    Public Class ExDataGridViewWarekiTextBoxColumn
        Inherits System.Windows.Forms.DataGridViewTextBoxColumn

        Public Sub New()
            MyBase.New()
            Me.CellTemplate = New clsDGVWarekiTextBoxCell
        End Sub
    End Class

    Imports System.Globalization
    Imports System.Windows.Forms

    Public Class clsDGVWarekiTextBoxCell
        Inherits DataGridViewTextBoxCell

        Shared Sub New()
            jpcul = New CultureInfo("ja-jp", True)
            jpcul.DateTimeFormat.Calendar = New JapaneseCalendar()
        End Sub

        Private Shared jpcul As CultureInfo

        Protected Overrides Function GetFormattedValue(ByVal value As Object _
                                                       , ByVal rowIndex As Integer _
                                                       , ByRef cellStyle As DataGridViewCellStyle _
                                                       , ByVal valueTypeConverter As System.ComponentModel.TypeConverter _
                                                       , ByVal formattedValueTypeConverter As System.ComponentModel.TypeConverter _
                                                       , ByVal context As DataGridViewDataErrorContexts) As Object
            If value Is Nothing OrElse value Is DBNull.Value Then
                Return String.Empty
            End If
            Dim dt As DateTime = CType(value, DateTime)
            Try
                Return dt.ToString("ggyy年MM月dd日", jpcul)
            Catch
                Me.ErrorText = "日付が和暦に変換できません"
                Return dt.ToLongDateString()
            End Try
        End Function

        Public Overrides Function ParseFormattedValue(ByVal formattedValue As Object _
                                                      , ByVal cellStyle As DataGridViewCellStyle _
                                                      , ByVal formattedValueTypeConverter As System.ComponentModel.TypeConverter _
                                                      , ByVal valueTypeConverter As System.ComponentModel.TypeConverter) As Object
            Dim ret As Object
            Me.ErrorText = String.Empty

            '編集で入力された文字列をDateTimeに変換
            Dim dt As DateTime
            Dim str As String = CType(formattedValue, String).Trim()
            If (String.IsNullOrEmpty(str)) Then
                ret = DBNull.Value
            ElseIf ((Char.IsNumber(str.Chars(0)) AndAlso DateTime.TryParseExact(str, "yyyy/M/d", DateTimeFormatInfo.CurrentInfo, DateTimeStyles.AllowWhiteSpaces, dt)) _
                OrElse DateTime.TryParse(str, jpcul, DateTimeStyles.AllowWhiteSpaces, dt)) Then
                ret = dt
            Else
                Const errmsg As String = "日付形式で入力をして下さい" & vbCrLf & "2009/1/1 又はH21/1/1"
                'Dim ex As New ApplicationException(errmsg)
                'MyBase.RaiseDataError(New DataGridViewDataErrorEventArgs(ex, Me.ColumnIndex, Me.RowIndex, DataGridViewDataErrorContexts.Parsing))
                Me.ErrorText = errmsg
                ret = MyBase.GetValue(RowIndex)
            End If

            Return ret
        End Function

        Public Overrides Sub InitializeEditingControl(ByVal rowIndex As Integer _
                                                      , ByVal initialFormattedValue As Object _
                                                      , ByVal dataGridViewCellStyle As DataGridViewCellStyle)
            If rowIndex = -1 Then
                Exit Sub
            Else
                '編集する時は元データを西暦で表示
                Dim value As Object = MyBase.GetValue(rowIndex)
                If value Is Nothing OrElse value Is DBNull.Value Then
                    value = String.Empty
                Else
                    value = ToYYYYMMDD(CType(value, DateTime))
                End If
                MyBase.InitializeEditingControl(rowIndex, value, dataGridViewCellStyle)
            End If
            
        End Sub

        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 DataGridViewElementStates, ByVal value As Object _
                                      , ByVal formattedValue As Object, ByVal errorText As String _
                                      , ByVal cellStyle As DataGridViewCellStyle, ByVal advancedBorderStyle As DataGridViewAdvancedBorderStyle _
                                      , ByVal paintParts As DataGridViewPaintParts)
            If ((cellState And DataGridViewElementStates.Selected) = DataGridViewElementStates.Selected) _
            AndAlso value IsNot Nothing AndAlso value IsNot DBNull.Value Then
                '選択されているセルは西暦表示させる
                formattedValue = ToYYYYMMDD(CType(value, DateTime))
            End If
            MyBase.Paint(graphics, clipBounds, cellBounds, rowIndex, cellState, value, formattedValue, errorText, cellStyle, advancedBorderStyle, paintParts)
        End Sub

        Private Function ToYYYYMMDD(ByVal dt As DateTime) As String
            Return CType(Value, DateTime).ToString("yyyy/MM/dd")
        End Function
    End Class

    で、一番下の 
    Function ToYYYYMMDD
    のところで、エラーとなっていまいます。

    現状の対処としては、質問の最後のところでも記載をしましたが、上記のコードにて
    DataGridViewのColumnTypeで和暦表示用のテキストボックスが使用できるようになったので、
    こちらを使用しているのですが、このテキストボックスがDataGridViewの最初の列になると
    エラーになってしまうので、別の列が最初の列になるように順序を変えて、表示をするようにしております。

    よろしく御願い致します。

    2014年9月4日 1:31
  • > で、一番下の 
    > Function ToYYYYMMDD
    > のところで、エラーとなっていまいます。

    そのコードは正しいのでしょうか? Value は何処から来てるのですか? 引数の dt はどうしたんですか? そもそもそのコードのその場所で "System.ArgumentOutOfRangeException:指定された引数は、有効な値の範囲内にありません。" というエラーが起こるとは思えませんが?

    そこを見ただけで自分の頭がコンパイルエラーになって思考停止してしまいました。(正直に言えば、それ以上見る気がしなくなりました。)

    たぶん間違っているんだと思いますが、もしそうなら、少なくともコードは一字一句間違えないようにしてください。


    【追伸】

    それから、質問者さんの開発環境も書いてください。(前のスレッドを見れば分かるとは言わないでくださいね)

    • 編集済み SurferOnWww 2014年9月4日 2:22 追伸追加
    2014年9月4日 2:20
  • 実際にコンパイル可能でそのエラーが出るからプログラミングって難しいですよねー。ValueはMe.Valueって書くとわかりやすいですね。

    SurferOnWwwさんも指摘されていますが、dtを無視してなぜかMe.Valueを見に行っているのが原因です。

    Paintはヘッダも含めてすべてのセルで呼び出されますが、Paint引数で渡されるrowIndexと、自分のインスタンスのプロパティのRowIndexが一致するとは限らないようです。つまり、このときrowIndexは1とかが渡されてきても、Paintを実行しているのはRowIndex=-1である可能性がある。そのとき、ToYYYYMMDDでMe.Valueを参照したら当然エラーになります。

    • 回答としてマーク TI-cb400 2014年9月4日 7:11
    2014年9月4日 3:21
  • ToYYYYMMDDのバグです。
    DataGridViewのSelectionModeによってエラーになったりならなかったりするようで、初期状態だとエラーにならなかったようです。
    あと、他の所にもSelectionModeの影響があったので修正です。

    #Me.Valueを参照してしまっているのは別関数に切り出した時に変更し忘れたのだと思う。

    Imports System.Globalization
    Imports System.Windows.Forms
    Public Class Form1
        Inherits Form
        Public Sub New()
            Me.InitializeComponent()
    
            Dim dt As New DataTable
            dt.Columns.Add("Column1", GetType(DateTime))
            dt.Rows.Add(DateTime.Now())
    
            Dim grid As New DataGridView()
            Dim dgvclm As New DataGridViewTextBoxColumn()
            dgvclm.DataPropertyName = "Column1"
            dgvclm.CellTemplate = New DGVWarekiTextBoxCell()
            grid.Columns.Add(dgvclm)
            grid.DataSource = dt
            grid.Dock = DockStyle.Fill
            Me.Controls.Add(grid)
            dgvclm.SortMode = DataGridViewColumnSortMode.NotSortable
            grid.SelectionMode = DataGridViewSelectionMode.FullColumnSelect
            'grid.SelectionMode = DataGridViewSelectionMode.FullRowSelect
    
        End Sub
    
        Friend Class DGVWarekiTextBoxCell
            Inherits DataGridViewTextBoxCell
    
            Shared Sub New()
                jpcul = New CultureInfo("ja-jp", True)
                jpcul.DateTimeFormat.Calendar = New JapaneseCalendar()
            End Sub
    
            Private Shared jpcul As CultureInfo
            Protected Overrides Function GetFormattedValue(ByVal value As Object _
                                                           , ByVal rowIndex As Integer _
                                                           , ByRef cellStyle As DataGridViewCellStyle _
                                                           , ByVal valueTypeConverter As System.ComponentModel.TypeConverter _
                                                           , ByVal formattedValueTypeConverter As System.ComponentModel.TypeConverter _
                                                           , ByVal context As DataGridViewDataErrorContexts) As Object
                If value Is Nothing OrElse value Is DBNull.Value Then
                    Return String.Empty
                End If
                Dim dt As DateTime = CType(value, DateTime)
                Try
                    Return dt.ToString("ggyy年MM月dd日", jpcul)
                Catch
                    Me.ErrorText = "日付が和暦に変換できません"
                    Return dt.ToLongDateString()
                End Try
            End Function
    
            Public Overrides Function ParseFormattedValue(ByVal formattedValue As Object _
                                                          , ByVal cellStyle As DataGridViewCellStyle _
                                                          , ByVal formattedValueTypeConverter As System.ComponentModel.TypeConverter _
                                                          , ByVal valueTypeConverter As System.ComponentModel.TypeConverter) As Object
                Dim ret As Object
                Me.ErrorText = String.Empty
    
                '編集で入力された文字列をDateTimeに変換
                Dim dt As DateTime
                Dim str As String = CType(formattedValue, String).Trim()
                If (String.IsNullOrEmpty(str)) Then
                    ret = DBNull.Value
                ElseIf ((Char.IsNumber(str.Chars(0)) AndAlso DateTime.TryParseExact(str, "yyyy/M/d", DateTimeFormatInfo.CurrentInfo, DateTimeStyles.AllowWhiteSpaces, dt)) _
                    OrElse DateTime.TryParse(str, jpcul, DateTimeStyles.AllowWhiteSpaces, dt)) Then
                    ret = dt
                Else
                    Const errmsg As String = "日付形式で入力をして下さい" & vbCrLf & "2009/1/1 又はH21/1/1"
                    'Dim ex As New ApplicationException(errmsg)
                    'MyBase.RaiseDataError(New DataGridViewDataErrorEventArgs(ex, Me.ColumnIndex, Me.RowIndex, DataGridViewDataErrorContexts.Parsing))
                    Me.ErrorText = errmsg
    
                    '@2014/9/4
                    'SelectionModeがFullRowSelect,FullColumnSelectの場合に
                    '起動直後にCells(0,0)に日付に変換できない文字を入れるとRowIndexに-1が入った状態になっている
                    'その状態でGetValueを行うとエラーになる
                    'ret = MyBase.GetValue(RowIndex)
                    If (RowIndex >= 0) Then
                        ret = MyBase.GetValue(RowIndex)
                    Else
                        ret = DBNull.Value
                    End If
                End If
    
                Return ret
            End Function
    
            Public Overrides Sub InitializeEditingControl(ByVal rowIndex As Integer _
                                                          , ByVal initialFormattedValue As Object _
                                                          , ByVal dataGridViewCellStyle As DataGridViewCellStyle)
                '編集する時は元データを西暦で表示
                '2014/9/4
                'ここでRowIndexが-1はありえないと思うけど念のため
                If (rowIndex >= 0) Then
                    Dim value As Object = MyBase.GetValue(rowIndex)
                    If value Is Nothing OrElse value Is DBNull.Value Then
                        value = String.Empty
                    Else
                        value = ToYYYYMMDD(CType(value, DateTime))
                    End If
                End If
                MyBase.InitializeEditingControl(rowIndex, Value, dataGridViewCellStyle)
            End Sub
    
            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 DataGridViewElementStates, ByVal value As Object _
                                          , ByVal formattedValue As Object, ByVal errorText As String _
                                          , ByVal cellStyle As DataGridViewCellStyle, ByVal advancedBorderStyle As DataGridViewAdvancedBorderStyle _
                                          , ByVal paintParts As DataGridViewPaintParts)
                If ((cellState And DataGridViewElementStates.Selected) = DataGridViewElementStates.Selected) _
                AndAlso value IsNot Nothing AndAlso value IsNot DBNull.Value Then
                    'AndAlso rowIndex >= 0 Then
                    '選択されているセルは西暦表示させる
    
                    formattedValue = ToYYYYMMDD(CType(value, DateTime))
                End If
                MyBase.Paint(graphics, clipBounds, cellBounds, rowIndex, cellState, value, formattedValue, errorText, cellStyle, advancedBorderStyle, paintParts)
            End Sub
    
            Private Function ToYYYYMMDD(ByVal dt As DateTime) As String
                '@2014/9/4
                'dtを使わずにMe.Valueを間違えて使ってたバグ
                'SelectionModeがFullRowSelect,FullColumnSelectの場合にMe.RowIndex=1になっていることがあって、Me.Valueがないことがある
                'Return CType(Value, DateTime).ToString("yyyy/MM/dd")
                Return CType(dt, DateTime).ToString("yyyy/MM/dd")
            End Function
        End Class
    End Class


    個別に明示されていない限りgekkaがフォーラムに投稿したコードにはフォーラム使用条件に基づき「MICROSOFT LIMITED PUBLIC LICENSE」が適用されます。(かなり自由に使ってOK!)

    • 回答としてマーク TI-cb400 2014年9月4日 7:11
    2014年9月4日 4:07
  • ご回答有難うございます。

    環境は、VB2010+SQLServerです。

    Funtion ToYYYYMMDDの「Value」を「dt」に変更したところ、1列目に和暦テキストボックスを配置しても
    エラーが出なくなりました。

    本当にありがとうございました。

    2014年9月4日 7:16