DataGridViewで自然順ソート
投稿者 魔界の仮面弁士  (社会人)
投稿日時
2021/6/4 12:17:09
> エクスプローラのファイル順
この順序は、 StrCmpLogicalW API の動作によるものです。
https://docs.microsoft.com/en-us/windows/win32/api/shlwapi/nf-shlwapi-strcmplogicalw
https://wiki.dobon.net/index.php?.NET%A5%D7%A5%ED%A5%B0%A5%E9%A5%DF%A5%F3%A5%B0%B8%A6%B5%E6%2F111
Windows バージョンによって並び順が変化することにご注意ください。
レジストリ設定すれば、StrCmpLogical を使わないようにすることもできます。
https://316-jp.com/windows-sort-name
StrCmpLogicalW などによるカスタムソートを DataGridView に組み込む場合、
DataSource が未設定なら、SortCompare イベントを利用できます。
https://dobon.net/vb/dotnet/datagridview/customsort.html#section3
DataSrouce を割り当てている場合は、そのデータソースの並び替え機構に依存します。
LINQ を使えるなら、OrderBy 拡張メソッドの IComparer(Of ) を受け取るオーバーロードを
併用するというのも一つのです。
この順序は、 StrCmpLogicalW API の動作によるものです。
https://docs.microsoft.com/en-us/windows/win32/api/shlwapi/nf-shlwapi-strcmplogicalw
https://wiki.dobon.net/index.php?.NET%A5%D7%A5%ED%A5%B0%A5%E9%A5%DF%A5%F3%A5%B0%B8%A6%B5%E6%2F111
Windows バージョンによって並び順が変化することにご注意ください。
レジストリ設定すれば、StrCmpLogical を使わないようにすることもできます。
https://316-jp.com/windows-sort-name
StrCmpLogicalW などによるカスタムソートを DataGridView に組み込む場合、
DataSource が未設定なら、SortCompare イベントを利用できます。
https://dobon.net/vb/dotnet/datagridview/customsort.html#section3
DataSrouce を割り当てている場合は、そのデータソースの並び替え機構に依存します。
LINQ を使えるなら、OrderBy 拡張メソッドの IComparer(Of ) を受け取るオーバーロードを
併用するというのも一つのです。
投稿者 たなやん  (社会人)
投稿日時
2021/6/4 14:57:29
お返事ありがとうございます。
ヘッダーをクリックした時にSortCompareイベントでソートを行う動作を設定できる事を理解出来ました。
紹介頂いたStrCmpLogicalW APIを利用してソートを行うのが簡単そうなので、これを利用しようとしてみましたが正しい並び替えになりませんでした。
APIの呼び出し方が間違っているのでしょうか?
また降順にしたい時はAPIの呼び出しにオプション引数等が必要なのでしょうか?
[code]
Private Sub DataGridView1_SortCompare(ByVal sender As Object, ByVal e As DataGridViewSortCompareEventArgs) Handles DataGridView1.SortCompare
DataGridView1.Sort(New LogicalStringComparer())
e.Handled = True
End Sub
Public Class LogicalStringComparer
Implements System.Collections.IComparer
Implements System.Collections.Generic.IComparer(Of String)
<System.Runtime.InteropServices.DllImport("shlwapi.dll",
CharSet:=System.Runtime.InteropServices.CharSet.Unicode,
ExactSpelling:=True)>
Private Shared Function StrCmpLogicalW(x As String, y As String) As Integer
End Function
Public Function Compare(x As String, y As String) As Integer _
Implements System.Collections.Generic.IComparer(Of String).Compare
Return StrCmpLogicalW(x, y)
End Function
Public Function Compare(x As Object, y As Object) As Integer _
Implements System.Collections.IComparer.Compare
Return Me.Compare(x.ToString(), y.ToString())
End Function
End Class
[/code]
ヘッダーをクリックした時にSortCompareイベントでソートを行う動作を設定できる事を理解出来ました。
紹介頂いたStrCmpLogicalW APIを利用してソートを行うのが簡単そうなので、これを利用しようとしてみましたが正しい並び替えになりませんでした。
APIの呼び出し方が間違っているのでしょうか?
また降順にしたい時はAPIの呼び出しにオプション引数等が必要なのでしょうか?
[code]
Private Sub DataGridView1_SortCompare(ByVal sender As Object, ByVal e As DataGridViewSortCompareEventArgs) Handles DataGridView1.SortCompare
DataGridView1.Sort(New LogicalStringComparer())
e.Handled = True
End Sub
Public Class LogicalStringComparer
Implements System.Collections.IComparer
Implements System.Collections.Generic.IComparer(Of String)
<System.Runtime.InteropServices.DllImport("shlwapi.dll",
CharSet:=System.Runtime.InteropServices.CharSet.Unicode,
ExactSpelling:=True)>
Private Shared Function StrCmpLogicalW(x As String, y As String) As Integer
End Function
Public Function Compare(x As String, y As String) As Integer _
Implements System.Collections.Generic.IComparer(Of String).Compare
Return StrCmpLogicalW(x, y)
End Function
Public Function Compare(x As Object, y As Object) As Integer _
Implements System.Collections.IComparer.Compare
Return Me.Compare(x.ToString(), y.ToString())
End Function
End Class
[/code]
投稿者 魔界の仮面弁士  (社会人)
投稿日時
2021/6/4 16:19:23
> [code]
code ではなく CODE です。
> 正しい並び替えになりませんでした。
複数列の並び替えが必要な場合は IComparer が必要ですが、
単一列の並び替えなら SortCompare イベントだけで済みますよ。
code ではなく CODE です。
> 正しい並び替えになりませんでした。
複数列の並び替えが必要な場合は IComparer が必要ですが、
単一列の並び替えなら SortCompare イベントだけで済みますよ。
Public Class Form1
Private WithEvents dgv As DataGridView
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
dgv = New DataGridView() With {.Dock = DockStyle.Fill}
dgv.AllowUserToAddRows = False
dgv.ColumnCount = 2
Controls.Add(dgv)
dgv.Rows.Add(1, "file1.txt")
dgv.Rows.Add(2, "file10.txt")
dgv.Rows.Add(3, "file2.txt")
dgv.Rows.Add(4, "file3.txt")
End Sub
Private Sub dgv_SortCompare(sender As Object, e As DataGridViewSortCompareEventArgs) Handles dgv.SortCompare
If e.Column.Index = 1 Then
e.SortResult = StrCmpLogicalW(e.CellValue1, e.CellValue2)
e.Handled = True
Else
e.Handled = False
End If
End Sub
Private Declare Unicode Function StrCmpLogicalW Lib "shlwapi" (x As String, y As String) As Integer
End Class
投稿者 魔界の仮面弁士  (社会人)
投稿日時
2021/6/4 16:36:07
追記:
先のコードは、列の SortMode プロパティが Automatic という前提です。
> e.SortResult = StrCmpLogicalW(e.CellValue1, e.CellValue2)
Option Strict On の場合は上記を
e.SortResult = StrCmpLogicalW(e.CellValue1?.ToString(), e.CellValue2?.ToString())
にします。
> また降順にしたい時はAPIの呼び出しにオプション引数等が必要なのでしょうか?
DataGridView 側でソート方向を見繕うので、呼び方は変わらないです。
ソートモードをプログラム制御する場合は、ColumnHeaderMouseClick イベントにて、
たなやんさんが書かれたように DataGridView1.Sort(System.Collections.IComparer) を呼び出します。
この場合、IComparer.Compare メソッドの引数は DataGridViewRow 型となりますので、
やろうと思えば複数列によるソートを実装することも出来るでしょう。
複数列ソートの場合に、ソート方向を表す三角グリフを描画したい場合には、
dgv.Columns(0).HeaderCell.SortGlyphDirection = SortOrder.Ascending
dgv.Columns(1).HeaderCell.SortGlyphDirection = SortOrder.Descending
などとして個別にマークします。
先のコードは、列の SortMode プロパティが Automatic という前提です。
> e.SortResult = StrCmpLogicalW(e.CellValue1, e.CellValue2)
Option Strict On の場合は上記を
e.SortResult = StrCmpLogicalW(e.CellValue1?.ToString(), e.CellValue2?.ToString())
にします。
> また降順にしたい時はAPIの呼び出しにオプション引数等が必要なのでしょうか?
DataGridView 側でソート方向を見繕うので、呼び方は変わらないです。
ソートモードをプログラム制御する場合は、ColumnHeaderMouseClick イベントにて、
たなやんさんが書かれたように DataGridView1.Sort(System.Collections.IComparer) を呼び出します。
この場合、IComparer.Compare メソッドの引数は DataGridViewRow 型となりますので、
やろうと思えば複数列によるソートを実装することも出来るでしょう。
複数列ソートの場合に、ソート方向を表す三角グリフを描画したい場合には、
dgv.Columns(0).HeaderCell.SortGlyphDirection = SortOrder.Ascending
dgv.Columns(1).HeaderCell.SortGlyphDirection = SortOrder.Descending
などとして個別にマークします。
投稿者 たなやん  (社会人)
投稿日時
2021/6/4 19:01:33
ありがとうございます。
目的は単一列の並び替えでしたので、ご提示頂いたコードで無事に希望通りの並び替えが出来ました。
プログラミング次第で複雑なソートも可能という事も知りとても勉強になります。
>Option Strict On の場合は上記を
> e.SortResult = StrCmpLogicalW(e.CellValue1?.ToString(), e.CellValue2?.ToString())
>にします。
Option StrictはOnでしたので上記に変更しましたところエラーは消えました。
ただこのe.CellValue1とe.CellValue2の後ろに「?」を付けても付けなくても結果は同じでしたが、何か意味があるのでしょうか?
目的は単一列の並び替えでしたので、ご提示頂いたコードで無事に希望通りの並び替えが出来ました。
プログラミング次第で複雑なソートも可能という事も知りとても勉強になります。
>Option Strict On の場合は上記を
> e.SortResult = StrCmpLogicalW(e.CellValue1?.ToString(), e.CellValue2?.ToString())
>にします。
Option StrictはOnでしたので上記に変更しましたところエラーは消えました。
ただこのe.CellValue1とe.CellValue2の後ろに「?」を付けても付けなくても結果は同じでしたが、何か意味があるのでしょうか?
投稿者 魔界の仮面弁士  (社会人)
投稿日時
2021/6/4 20:36:05
> ただこのe.CellValue1とe.CellValue2の後ろに「?」を付けても付けなくても結果は同じでしたが、何か意味があるのでしょうか?
先のサンプルに
dgv.Rows.Add(5, Nothing)
dgv.Rows.Add(6, DBNull.Value)
を含めた状態で、 ? の有無を確認してみてください。
先のサンプルに
dgv.Rows.Add(5, Nothing)
dgv.Rows.Add(6, DBNull.Value)
を含めた状態で、 ? の有無を確認してみてください。
Dim x = e.CellValue1?.ToString()
上記のコードは下記と同じ意味です。Dim x As String = If(e.CellValue1 IsNot Nothing, e.CellValue1.ToString(), Nothing)
投稿者 たなやん  (社会人)
投稿日時
2021/6/4 23:24:31
なるほど、変数の後ろに?を付ければ文字列がnullやNothingだった場合の条件式を書く必要がなくなるんですね。また一つ勉強になりました。
VBはまだまだ分からない事が多いですが、今後ともよろしくお願いします。
VBはまだまだ分からない事が多いですが、今後ともよろしくお願いします。
投稿者 たなやん  (社会人)
投稿日時
2021/6/22 19:35:20
引き続きすみません、データ管理の都合によりDataGridViewにDataSrouceを割り当てて表示するように仕様変更をしたところ自然順ソートが機能しなくなりました。
調べてみたところDataSourceを割り当てるとSortCompareイベントが作動しなくなるようなので、余談で教えて頂いたColumnHeaderMouseClickイベントでプログラム制御する方法ならソートが出来そうなのですが、この場合でのStrCmpLogicalW APIの呼び出し方が分かりません。
お手数おかけしますが再度ご教授をよろしくお願いいたします。
調べてみたところDataSourceを割り当てるとSortCompareイベントが作動しなくなるようなので、余談で教えて頂いたColumnHeaderMouseClickイベントでプログラム制御する方法ならソートが出来そうなのですが、この場合でのStrCmpLogicalW APIの呼び出し方が分かりません。
お手数おかけしますが再度ご教授をよろしくお願いいたします。
Public Declare Unicode Function StrCmpLogicalW Lib "shlwapi" (x As String, y As String) As Integer
Private bFlag As Boolean '昇順、降順フラグ
Private Sub DataGridView1_ColumnHeaderMouseClick(sender As Object, e As DataGridViewCellMouseEventArgs) Handles DataGridView1.ColumnHeaderMouseClick
Dim dt As DataTable = CType(DataGridView1.DataSource, DataTable)
Dim dv As DataView = dt.DefaultView
bFlag = Not bFlag
For Each SelectedColumn As DataGridViewColumn In DataGridView1.SelectedColumns
If SelectedColumn.Index = 0 Then
'dv.Sort = StrCmpLogicalW(dv.CellValue1?.ToString(), dv.CellValue2?.ToString()) '1列目のソートAPI呼び出し(エラー)
If bFlag Then
DataGridView1.Columns(0).HeaderCell.SortGlyphDirection = SortOrder.Ascending
Else
DataGridView1.Columns(0).HeaderCell.SortGlyphDirection = SortOrder.Descending
End If
ElseIf SelectedColumn.Index = 1 Then
'dv.Sort = StrCmpLogicalW(dv.CellValue1?.ToString(), dv.CellValue2?.ToString()) '2列目のソートAPI呼び出し(エラー)
If bFlag Then
DataGridView1.Columns(1).HeaderCell.SortGlyphDirection = SortOrder.Ascending
Else
DataGridView1.Columns(1).HeaderCell.SortGlyphDirection = SortOrder.Descending
End If
End If
Next
DataGridView1.DataSource = dv
End Sub
投稿者 魔界の仮面弁士  (社会人)
投稿日時
2021/6/23 14:34:05
File テーブルの 0 列目にある "FullPath" フィールドで並び替える例を書いてみました。
ここでは単一列で並び替えていますが、もしも複数列で並び替えるなら、
Overrides Function Compare 内の処理を増やすか、もしくは
ColumnHeaderMouseClick で LINQ の ThenBy を併用すれば OK です。
ここでは単一列で並び替えていますが、もしも複数列で並び替えるなら、
Overrides Function Compare 内の処理を増やすか、もしくは
ColumnHeaderMouseClick で LINQ の ThenBy を併用すれば OK です。
Private ds As DataeSet 'DataSet/DataTable の構築部は省略
Private Sub 何某()
Me.DataGridView1.DataSource = Me.ds.Tables("File") 'File テーブルがバインドされていたとする
Me.DataGridView1.Columns(0).SortMode = DataGridViewColumnSortMode.Programmatic
End Sub
Private Sub DataGridView1_ColumnHeaderMouseClick(sender As Object, e As DataGridViewCellMouseEventArgs) Handles DataGridView1.ColumnHeaderMouseClick
If e.ColumnIndex = 0 Then
Dim order = SortOrder.Ascending
If Me.DataGridView1.Columns(0).HeaderCell.SortGlyphDirection = SortOrder.Ascending Then
order = SortOrder.Descending
End If
Dim q = Me.ds.Tables("File").AsEnumerable()
q = q.OrderBy(Function(r) r, New RowSorter(order, "FullPath")) 'LINQ で並び替え
Me.DataGridView1.DataSource = q.CopyToDataTable() 'DataTable に復元
Me.DataGridView1.Columns(0).HeaderCell.SortGlyphDirection = order
End If
End Sub
Private Class RowSorter
Inherits Comparer(Of DataRow)
Private Declare Unicode Function StrCmpLogicalW Lib "shlwapi" (x As String, y As String) As Integer
Private ReadOnly order As SortOrder
Private ReadOnly fieldName As String
Public Sub New(order As SortOrder, fieldName As String)
Me.order = order
Me.fieldName = fieldName
End Sub
Public Overrides Function Compare(x As DataRow, y As DataRow) As Integer
If order = SortOrder.Descending Then
Return StrCmpLogicalW(y(fieldName)?.ToString(), x(fieldName)?.ToString())
Else
Return StrCmpLogicalW(x(fieldName)?.ToString(), y(fieldName)?.ToString())
End If
End Function
End Class
投稿者 やなやん  (社会人)
投稿日時
2021/6/23 19:02:47
魔界の仮面弁士様、いつもありがとうございます。
仕様変更の説明が不足しておりました。
データの管理はDataTable一つのみで行っておりDataSetは使用していません。
ご提示いただいたコードにDataSetを作成してその中に管理しているDataTableとテーブル名を追加する事で自然順ソートが動作する事を確認できました。
ただそれに伴い他の全てのコードをDataSetに対応させなければならないため、大変恐縮なのですが直接DataTable内の単一列を並び替える方法があればそちらも教えて頂けないでしょうか。
よろしくお願いいたします。
仕様変更の説明が不足しておりました。
データの管理はDataTable一つのみで行っておりDataSetは使用していません。
ご提示いただいたコードにDataSetを作成してその中に管理しているDataTableとテーブル名を追加する事で自然順ソートが動作する事を確認できました。
ただそれに伴い他の全てのコードをDataSetに対応させなければならないため、大変恐縮なのですが直接DataTable内の単一列を並び替える方法があればそちらも教えて頂けないでしょうか。
よろしくお願いいたします。
投稿者 魔界の仮面弁士  (社会人)
投稿日時
2021/6/23 19:18:45
やなやんさんはたなやんさんの同僚の方なのでしょうか。
> データの管理はDataTable一つのみで行っておりDataSetは使用していません。
自分のコードもそうですよ?
DataSet をバインドしているのであれば、DataSource だけでなく、
DataMember も指定しているはず。
そもそも、
Dim tbl As DataTable = Me.ds.Tables("File")
ですよね。実質的に使っているのは DataTable だけです。
つまり、DataGridView1.DataSource に割り当てているのは DataTable ですし、
Dim q = Me.ds.Tables("File").AsEnumerable() というのも、
Dim q = Me.yourTable.AsEnumerable() でしかありせん。
そして LINQ でソート後、複数行の DataRow を DataTable に復元するのは、
先に示した CopyToDataTable 拡張メソッドです。
なお、DataSource に割り当てられるインスタンスは、
CopyToDataTable によって、毎回、新しいテーブルに差し変わります。
そのため、
> Dim dt As DataTable = CType(DataGridView1.DataSource, DataTable)
などとするのではなく、最初に取得した DataTable / DataSet / DataView 等を
フィールド変数に保持しておき、それをソート対象にするのが良いと思います。
> データの管理はDataTable一つのみで行っておりDataSetは使用していません。
自分のコードもそうですよ?
DataSet をバインドしているのであれば、DataSource だけでなく、
DataMember も指定しているはず。
そもそも、
Dim tbl As DataTable = Me.ds.Tables("File")
ですよね。実質的に使っているのは DataTable だけです。
つまり、DataGridView1.DataSource に割り当てているのは DataTable ですし、
Dim q = Me.ds.Tables("File").AsEnumerable() というのも、
Dim q = Me.yourTable.AsEnumerable() でしかありせん。
そして LINQ でソート後、複数行の DataRow を DataTable に復元するのは、
先に示した CopyToDataTable 拡張メソッドです。
なお、DataSource に割り当てられるインスタンスは、
CopyToDataTable によって、毎回、新しいテーブルに差し変わります。
そのため、
> Dim dt As DataTable = CType(DataGridView1.DataSource, DataTable)
などとするのではなく、最初に取得した DataTable / DataSet / DataView 等を
フィールド変数に保持しておき、それをソート対象にするのが良いと思います。
投稿者 たなやん  (社会人)
投稿日時
2021/6/23 22:21:53
ご回答ありがとうございます。
名前を記入する際にTとYのキーを打ち間違えていました。。
すみません、お恥ずかしい話データベースプログラミングは今回が初めてなのとLINQは未経験です。
> Private ds As DataeSet
ご提示いただいたコードの最初の行でDataSetを宣言されていたのと
> Dim q = Me.ds.Tables("File").AsEnumerable()
宣言されたdsのFileテーブルに対してメソッドを呼び出されていたのでDataSetにして使用できないかと思っておりました。
先ほどAsEnumerableについて調べてみたところ、これはLINQのメソッドでDataTableにも使用できるのですね。
大変失礼しました。
データを管理しているDataTableはグロバール変数に保持していますので、
下記のように書き換えてみたところDataTable自体の並び替えが出来ました、ありがとうございます。
Dim q = MyDataTable.AsEnumerable()
余談なのですがソートする際に、こちらのテーブル列(FullPath)を文字列ではなく列番号で指定することは可能でしょうか?
> q = q.OrderBy(Function(r) r, New RowSorter(order, "FullPath")) 'LINQ で並び替え
名前を記入する際にTとYのキーを打ち間違えていました。。
すみません、お恥ずかしい話データベースプログラミングは今回が初めてなのとLINQは未経験です。
> Private ds As DataeSet
ご提示いただいたコードの最初の行でDataSetを宣言されていたのと
> Dim q = Me.ds.Tables("File").AsEnumerable()
宣言されたdsのFileテーブルに対してメソッドを呼び出されていたのでDataSetにして使用できないかと思っておりました。
先ほどAsEnumerableについて調べてみたところ、これはLINQのメソッドでDataTableにも使用できるのですね。
大変失礼しました。
データを管理しているDataTableはグロバール変数に保持していますので、
下記のように書き換えてみたところDataTable自体の並び替えが出来ました、ありがとうございます。
Dim q = MyDataTable.AsEnumerable()
余談なのですがソートする際に、こちらのテーブル列(FullPath)を文字列ではなく列番号で指定することは可能でしょうか?
> q = q.OrderBy(Function(r) r, New RowSorter(order, "FullPath")) 'LINQ で並び替え
投稿者 魔界の仮面弁士  (社会人)
投稿日時
2021/6/24 10:47:38
> テーブル列(FullPath)を文字列ではなく列番号で指定することは可能でしょうか?
そういう実装に書き換えるだけで良いと思いますが、どの点が問題になっているのでしょうか?
先のサンプルで、列番号ではなくあえて列名を使用したのは、
「DataGridView の列番号」と「DataTable の列番号」を混同しないようにするため、
あえて違う型にしただけです。
たとえば、DataGridView にバインドさせていない非表示列をソート用に使うこともできますが、
まず、クリックした列をそのままソートに使いたいのであれば、
Dim fieldName = DataGridView1.Columns(e.ColumnIndex).DataPropertyName
のようにして、DataTable の列名を受け取ることができます。
そもそも、今回のソートの肝となるのは
>> Public Overrides Function Compare(x As DataRow, y As DataRow) As Integer
に対する
>> Return StrCmpLogicalW(x(fieldName)?.ToString(), y(fieldName)?.ToString())
なわけですよね。
DataRow のインデクサには、
『列名』な String
『列番号』な Integer
『列オブジェクト』な DataColumn
のいずれも受け付けるので、列番号で渡せるようにしたいのであれば、
単にそういう型の変数や引数を用意するだけで、列番号指定バージョンに作り替えられるでしょう。
https://docs.microsoft.com/ja-jp/dotnet/api/system.data.datarow.item?view=netframework-4.8#System_Data_DataRow_Item_System_Data_DataColumn_
そういう実装に書き換えるだけで良いと思いますが、どの点が問題になっているのでしょうか?
先のサンプルで、列番号ではなくあえて列名を使用したのは、
「DataGridView の列番号」と「DataTable の列番号」を混同しないようにするため、
あえて違う型にしただけです。
たとえば、DataGridView にバインドさせていない非表示列をソート用に使うこともできますが、
まず、クリックした列をそのままソートに使いたいのであれば、
Dim fieldName = DataGridView1.Columns(e.ColumnIndex).DataPropertyName
のようにして、DataTable の列名を受け取ることができます。
そもそも、今回のソートの肝となるのは
>> Public Overrides Function Compare(x As DataRow, y As DataRow) As Integer
に対する
>> Return StrCmpLogicalW(x(fieldName)?.ToString(), y(fieldName)?.ToString())
なわけですよね。
DataRow のインデクサには、
『列名』な String
『列番号』な Integer
『列オブジェクト』な DataColumn
のいずれも受け付けるので、列番号で渡せるようにしたいのであれば、
単にそういう型の変数や引数を用意するだけで、列番号指定バージョンに作り替えられるでしょう。
https://docs.microsoft.com/ja-jp/dotnet/api/system.data.datarow.item?view=netframework-4.8#System_Data_DataRow_Item_System_Data_DataColumn_
投稿者 たなやん  (社会人)
投稿日時
2021/6/24 20:44:06
ご返事ありがとうございます。
文字列で列指定する方法で全く問題ありません。
もし列番号で指定したい場合にはどのように書けばよいのかふと疑問に思ったため余談でご質問させて頂きました。
捕捉についてもご丁寧なご解説を頂きとても分かりやすかったです。
ひとまずご提示頂いたコードで進めていきたいと思います。
ありがとうございました。
文字列で列指定する方法で全く問題ありません。
もし列番号で指定したい場合にはどのように書けばよいのかふと疑問に思ったため余談でご質問させて頂きました。
捕捉についてもご丁寧なご解説を頂きとても分かりやすかったです。
ひとまずご提示頂いたコードで進めていきたいと思います。
ありがとうございました。
DataGridViewに日付とファイル名の文字列を直接追加してファイルを管理するツールを作っています。
それぞれの列のヘッダーをクリックするとこのように文字列でソートされます。
file1.txt
file10.txt
file2.txt
file3.txt
これをエクスプローラのファイル順のような自然順で昇順・降順ソートを行いたいのですが、どうすればよいのでしょうか?
昇順ソート
file1.txt
file2.txt
file3.txt
file10.txt
降順ソート
file10.txt
file3.txt
file2.txt
file1.txt
宜しくお願いします。