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

ソート状態の自動並び替えを避けるためのコードのトラブル

環境/言語:[OS : Windows 7 / 言語 : Visual Basic .NET / .NET Framework : 4]
分類:[.NET]

【解決したい問題】

VS2013Expressを使用しています。
datagridviewにdatatableを表示しています。datasetは外部とは接続していません。

datagridviewで列をソートした状態でデータの編集をしたいのですが、自動並び替えをされてしまうので、入力直後に行が飛んでしまいます。
自動で並び替えをされないために、データ列をソートしたあと、別の列に連番を入れて、改めてその列をソートして、ソートしたばかりの状態を保とうとしているのですが、うまく行きません。

実際のプログラムでうまくいかないので別にテスト用に3列のみの表を作り、1列目がデータ、2列目がソート用、3列目には新規行作成時に連番を入れて、元の入力順に戻すためのソート用に作成しています。3列目のソートに関しては問題がないようです。

デバッグしてみると、連番を入れた列のソートがうまく行かず、例えば昇順の場合、5,1,4,3,2 など不明な並び方をします。その連番を入れた列ヘッダには昇順のアイコンが出ています。
妙なことに、その後すでにソート状態にないデータ列のデータを修正すると、突然正しい並び順になったりします。


具体的には以下の様なコードです。

Private Sub DataGridView1_ColumnHeaderMouseClick1(sender As Object, e As DataGridViewCellMouseEventArgs) Handles DataGridView1.ColumnHeaderMouseClick

If DataGridView1.RowCount = 0 Then
Exit Sub
End If

If e.ColumnIndex = 1 Then
Exit Sub
End If

If e.ColumnIndex = 2 Then
Exit Sub
End If

Dim CCol = DataGridView1.Columns(e.ColumnIndex)
Dim Sort1 = DataGridView1.Columns(1) 
Dim Sort2 = DataGridView1.Columns(2) '///オリジナル順
Dim r As Integer = DataGridView1.RowCount
Dim ss = TextBox1.Text

Select Case ss
Case 0
'目的の列を並び替え
DataGridView1.Sort(CCol, System.ComponentModel.ListSortDirection.Ascending)

'並び替え後、「sort1」に連番記入
For i = 0 To r - 2
DataGridView1.Item(1, i).Value = i + 1
Next

DataGridView1.Sort(Sort1, System.ComponentModel.ListSortDirection.Ascending)

TextBox1.Text = 1

Case 1
DataGridView1.Sort(CCol, System.ComponentModel.ListSortDirection.Descending)

For i = 0 To r - 2
DataGridView1.Item(1, i).Value = i + 1
Next

DataGridView1.Sort(Sort1, System.ComponentModel.ListSortDirection.Descending)

TextBox1.Text = 2

Case 2
DataGridView1.Sort(Sort2, System.ComponentModel.ListSortDirection.Ascending)

TextBox1.Text = 0

End Select
End Sub

それぞれのCaseでSort1のソートをコメントアウトしてデバッグしてみると連番はきちんと入っています。Sort1をソートするところで変になります。
見当がつかなくて困っております。どうぞよろしくお願い致します。
あるいは、ソート状態で自動並び替えされない他の方法がありましたらお教えいただければ幸いです。
どうも、連続でソートを行わせると問題があるようです。
1. 新しいDataViewをNewする
2. 新しいDataViewで該当列をソート
3. 新しいDataViewで連番列に連番を割り振る
4. バインディングで使用しているDataView(DataGridViewやBindingSourceのDataSourceにDataTableを直接設定している場合はDataTable::DefaultView)で連番列をソート
とやると、正しくソート結果が表示されます。

ただし、このようにデータソースでソートさせた場合、コミットされていない新規行についてはまだデータソースに追加されていない状態なのでソートの対象外になります。
それが不都合であれば、
http://social.msdn.microsoft.com/Forums/ja-JP/f1424a11-08a1-4e3e-88a3-64177f5534c0/datagridview-?forum=vbgeneralja
ここにあるような処理を、DataGridView::IsCurrentRowDirtyなときにソート前に追加する必要があります。
(必須列が未入力な状態とか考えると頭が混乱します)
■No31944に返信(moriさんの記事)
> 例えば昇順の場合、5,1,4,3,2 など不明な並び方をします。
どういうデータに対してどういう操作をしたときに 5,1,4,3,2 に並ぶのかが
分からないので、この情報から状況をつかむのは難しいです。

> 自動並び替えをされてしまうので、入力直後に行が飛んでしまいます。
並び替えが邪魔ならば、入力前にソートを解除しておいては如何でしょう。

で、具体的にはどういうデータの場合に、どうなって欲しいのでしょうか。

その飛んだ行にフォーカスがある状態になっていれば構わないのか、
それとも、新規行は常に末尾行のままになっていて欲しいのか。


もし、末尾行のままにしておきたいのだとしたら、行追加後に
既存行の修正を行った場合、その並びはどうなって欲しいのでしょうか。

新規行は末尾のままで、既存行だけでソーティングされるのが良いのか、
あるいは、今追加した行も含めてソーティングされるべきなのか、
あるいは既存行も新規行も一切ソートされない方がよいのか、
あるいは…。
■No31945に返信(Hongliangさんの記事)
Hongliangさん、返信有難うございます。

> どうも、連続でソートを行わせると問題があるようです。
> 1. 新しいDataViewをNewする
> 2. 新しいDataViewで該当列をソート
> 3. 新しいDataViewで連番列に連番を割り振る
> 4. バインディングで使用しているDataView(DataGridViewやBindingSourceのDataSourceにDataTableを直接設定している場合はDataTable::DefaultView)で連番列をソート
> とやると、正しくソート結果が表示されます。

DataViewの理解が乏しかったのですが、ご指摘いただいて非常に参考になりました。おかげさまで以下の様なコードで思うような動作をさせることが出来ました。
また、新規の行があるとやはりエラーが出ましたが、独自のボタンで直接DataTableに行を追加するようにしましたら、エラーが出ないように思います。
実際のプログラムでも、AllowUserToAddRowsはFalseに設定してあったので、テストプログラムも同様にしましたので、For i = 0 To r - 2 → For i = 0 To r - 1に変更しています。


////並べ替えの部分のコード


Dim view = New DataView
view.Table = DataSet1.DataTable1

Select Case ss
Case 0

'目的の列を並び替え(view)
view.Sort = ("c1 ASC")

'並び替え後、view「sort1」に連番記入
For i = 0 To r - 1
view(i)(1) = i
Next

DataGridView1.Sort(Sort1, System.ComponentModel.ListSortDirection.Ascending)

TextBox1.Text = 1

Case 1

'目的の列を並び替え(view)
view.Sort = ("c1 ASC")

'並び替え後、view「sort1」に連番記入
For i = 0 To r - 1
view(i)(1) = i
Next

DataGridView1.Sort(Sort1, System.ComponentModel.ListSortDirection.Descending)

TextBox1.Text = 2

Case 2
DataGridView1.Sort(Sort2, System.ComponentModel.ListSortDirection.Ascending)

TextBox1.Text = 0

End Select
view.Dispose()

////////行追加のボタンクリック
Dim dt As DataTable = DataSet1.DataTable1
dt.Rows.Add()

入力必須列はありません。入力のない行は色がついて目立つようにはなっています。
まだ問題点がありましたら、ご指摘いただけると幸いです。
ありがとうございます。
■No31946に返信(魔界の仮面弁士さんの記事)
魔界の仮面弁士さん、返信有難うございます。

>>例えば昇順の場合、5,1,4,3,2 など不明な並び方をします。
> どういうデータに対してどういう操作をしたときに 5,1,4,3,2 に並ぶのかが
> 分からないので、この情報から状況をつかむのは難しいです。

並び替えた順番を保持するために、別の列に並び替えた順番で連番を入れます。その列は、1,2,3,4,5・・・と入っているので、連番を昇順でソートすれば本来なら普通に1,2,3,4,5と並ぶはずがそうならなかったということです。

並び替えて、列のデータが未入力やエラーコードの入った行を一番上にまとめておいて、エラーのセル、未入力のセルを編集したかったのです。エラーのある場合は連動して他の列もエラーになっていることが多いので、一旦並び替えたら、修正したい行の編集が全て済んで、改めて元の並び順に戻すまでは、自動で並び替えられたくなかったということですが、説明がうまくなくてすみません。

今のところ、上記のコードでうまく行っているようにみえるのですが、もし例外の出そうな部分がありましたら、ご指摘いただけると幸いです。
ありがとうございます。
■No31944に返信(moriさんの記事)
魔界の仮面弁士さん、HongLiangさん、アドバイスありがとうございました。
実際のプログラムでは複数列ですが、うまくいきました。
ソート状態の判定もテキストボックスではなく、HeaderCell.SortGlyphDirectionをつけることで判定するようにして少しスマートになりました。
煮詰まっていましたのでほんとうに感謝です。
解決済み!

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