DataGridViewでのソート処理について
投稿者 魔界の仮面弁士  (社会人)
投稿日時
2024/1/29 18:19:47
> Long型 や Date型 に
ということは、Visual Basic での開発ですね。
DataGridView の DataSource を設定して使っていますか?(データバインド)
それとも、セル一つ一つに直接値をセットしていますか?(非バインド)
> セルに値を入力したあと
DataSet などをバインドしている場合は、DataTable の列で型を明示できます。
(個人的にはデータバインドで利用することをお奨めします)
そして画面から値を入力した場合は、基本的に『文字列』としての入力になりますので、
非バインドの場合は入力値をそのまま利用するのではなく、TryParse メソッドを併用するなどして、
入力された値を、「文字列型の 12345」を「整数型の 12345」に変更するとか
「文字列型の 2024/01/29」を「日付型の 2024/01/29」に変更するなどといった、
データ型の検査と変換処置が必要になります。
もちろん、ユーザーが編集した値だけでなく、プログラムでセットした値についても同じことが言えます。
新規フォームに、空の DataGridView を貼って、そのまま実行してみましょう。
ということは、Visual Basic での開発ですね。
DataGridView の DataSource を設定して使っていますか?(データバインド)
それとも、セル一つ一つに直接値をセットしていますか?(非バインド)
> セルに値を入力したあと
DataSet などをバインドしている場合は、DataTable の列で型を明示できます。
(個人的にはデータバインドで利用することをお奨めします)
そして画面から値を入力した場合は、基本的に『文字列』としての入力になりますので、
非バインドの場合は入力値をそのまま利用するのではなく、TryParse メソッドを併用するなどして、
入力された値を、「文字列型の 12345」を「整数型の 12345」に変更するとか
「文字列型の 2024/01/29」を「日付型の 2024/01/29」に変更するなどといった、
データ型の検査と変換処置が必要になります。
もちろん、ユーザーが編集した値だけでなく、プログラムでセットした値についても同じことが言えます。
新規フォームに、空の DataGridView を貼って、そのまま実行してみましょう。
'非バインドの場合
Public Class Form1
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Me.DataGridView1.ColumnCount = 1
Me.DataGridView1.RowCount = 4
Me.DataGridView1(0, 0).Value = 12L 'これらは Long 型(System.Int64 構造体)
Me.DataGridView1(0, 1).Value = 12345L
Me.DataGridView1(0, 2).Value = "12345" 'ここだけ、文字列型(System.String クラス)が混入している
Me.DataGridView1(0, 3).Value = 123L
' 見た目では区別がつきませんが、
' 実際のデータ型では Long / String の差異があることがわかります。
' MsgBox(TypeName(Me.DataGridView1(0, 1).Value))
' MsgBox(TypeName(Me.DataGridView1(0, 2).Value))
End Sub
End Class
'DataTableをバインドした場合
Public Class Form1
Private ds As New DataSet()
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Dim tbl = ds.Tables.Add("tbl")
tbl.Columns.Add("Num", GetType(Long))
tbl.Rows.Add(12L)
tbl.Rows.Add(12345L)
tbl.Rows.Add("12345") '本来は Long 型を渡すべきだが、あえて文字列側をセット
tbl.Rows.Add(123L)
ds.AcceptChanges()
Me.DataGridView1.DataSource = ds
Me.DataGridView1.DataMember = tbl.TableName
'この DataTable の Num 列は、予め Long 型である事を明示しているため、
'文字列が渡されても、自動的に Long 型に変換されて登録されています。
' MsgBox(TypeName(tbl.Rows(2)(0)))
' MsgBox(TypeName(Me.DataGridView1(0, 2).Value))
End Sub
End Class
投稿者 ヒロ  (学生)
投稿日時
2024/1/30 10:22:54
回答ありがとうございます。
サンプルプログラムで問題点が理解できました。
DataGridView には非バインドでデータをセルにセットしています。
TypeNameを使って確認したところ、Long型の列のセルにはプログラムでセットした値はすべてString型になっていることがわかりました。
逆に直接入力した値はInt64型(恐らくLong型のこと?)になっていました。
プログラムでセルに値をセットする処理でLong型に変換することで、ソート時のエラーを回避することができました。
本当にありがとうございます。
サンプルプログラムで問題点が理解できました。
DataGridView には非バインドでデータをセルにセットしています。
TypeNameを使って確認したところ、Long型の列のセルにはプログラムでセットした値はすべてString型になっていることがわかりました。
逆に直接入力した値はInt64型(恐らくLong型のこと?)になっていました。
プログラムでセルに値をセットする処理でLong型に変換することで、ソート時のエラーを回避することができました。
本当にありがとうございます。
投稿者 ヒロ  (社会人)
投稿日時
2024/1/30 10:25:48
問題が解決しましたので、解決チェックを入れておきます。
ありがとうございます。
ありがとうございます。
投稿者 魔界の仮面弁士  (社会人)
投稿日時
2024/1/30 12:42:41
> そして画面から値を入力した場合は、基本的に『文字列』としての入力になりますので、
> 非バインドの場合は入力値をそのまま利用するのではなく、TryParse メソッドを併用するなどして、
> 入力された値を、「文字列型の 12345」を「整数型の 12345」に変更するとか
> 「文字列型の 2024/01/29」を「日付型の 2024/01/29」に変更するなどといった、
> データ型の検査と変換処置が必要になります。
下記は非バインド状態の時に、0 列目に Long 値の入力を強制させる例です。
入力値は文字列型で入ってくるので、数値に変換なら Long.TryParse メソッドで変換します。
日付型の列ならば、Date.TryParseExact を使うなどしてみてください。
ただし、CellValidating/CellValidated イベントが効くのは、ユーザー操作に対してです。
プログラムから直接、
Me.DataGridView1.BeginEdit(True)
Me.DataGridView1(0, 0).Value = "1234" 'Long 値では無いがセットできてしまう
Me.DataGridView1.EndEdit()
などと値をセットした場合には、これらのイベントが呼び出されません。
プログラムからの直接入力の際には、値をセットする段階で検査し、
必要に応じて「正しいデータ型」の値に変換してから代入するようにします。
> 非バインドの場合は入力値をそのまま利用するのではなく、TryParse メソッドを併用するなどして、
> 入力された値を、「文字列型の 12345」を「整数型の 12345」に変更するとか
> 「文字列型の 2024/01/29」を「日付型の 2024/01/29」に変更するなどといった、
> データ型の検査と変換処置が必要になります。
下記は非バインド状態の時に、0 列目に Long 値の入力を強制させる例です。
入力値は文字列型で入ってくるので、数値に変換なら Long.TryParse メソッドで変換します。
日付型の列ならば、Date.TryParseExact を使うなどしてみてください。
Private Sub DataGridView1_CellValidating(sender As Object, e As DataGridViewCellValidatingEventArgs) Handles DataGridView1.CellValidating
''0列目は Long 値を必須とする。Long 値にできない文字列が入力されたら拒絶。
'If e.ColumnIndex = 0 AndAlso e.RowIndex >= 0 Then
' Dim n As Long = 0
' If Not Long.TryParse(e.FormattedValue, n) Then
' e.Cancel = Me.DataGridView1.NewRowIndex <> e.RowIndex
' End If
'End If
End Sub
Private Sub DataGridView1_CellValidated(sender As Object, e As DataGridViewCellEventArgs) Handles DataGridView1.CellValidated
'0列目の値が Long 型でなかった場合は、Long 値に変換する。
If e.ColumnIndex = 0 AndAlso e.RowIndex >= 0 Then
Dim v = Me.DataGridView1(0, e.RowIndex).Value
If TypeOf v IsNot Long Then
Dim n As Long = 0
If Long.TryParse(v?.ToString(), n) Then
Me.DataGridView1(0, e.RowIndex).Value = n
Else
'Me.DataGridView1(0, e.RowIndex).Value = DBNull.Value
End If
End If
End If
End Sub
ただし、CellValidating/CellValidated イベントが効くのは、ユーザー操作に対してです。
プログラムから直接、
Me.DataGridView1.BeginEdit(True)
Me.DataGridView1(0, 0).Value = "1234" 'Long 値では無いがセットできてしまう
Me.DataGridView1.EndEdit()
などと値をセットした場合には、これらのイベントが呼び出されません。
プログラムからの直接入力の際には、値をセットする段階で検査し、
必要に応じて「正しいデータ型」の値に変換してから代入するようにします。
String型の列では問題ないのですが、列のデータ型が Long型 や Date型 になると、セルに値を入力したあと、その列のヘッダをクリックしてソートすると、データ型の不一致でエラーになってしまいます。
CellEndEdit イベントで、編集したセルの内容やデータ型を取得してみましたが、列のデータ型と一緒しており、エラーの内容と異なり、問題箇所が特定できません。
また、金額列と日付列にはそれぞれ書式 ”0,#” と "yyyy/MM/dd" を設定しています。
これだけの情報では何とも言えないかと思います。
必要な情報があれば、わかる範囲で回答させていただきますので、解決方法を探る糸口をおしえていただけないでしょうか?