Discussion:
DataGridView und SelectedRows
(zu alt für eine Antwort)
Heiko Röbke
2007-05-31 15:07:25 UTC
Permalink
Hallo,

ich habe in meinem VB.Net Projekt ein DataGridView implementiert, in dem der
Benutzer eine oder mehrere Zeile markieren bzw. selektieren kann. Oder er
klickt in dem DataGridView in den kleinen Kasten links oben zum Selektieren
aller Zeilen im Grid. Das Grid enthält Daten, die nach einer ID aufsteigend
sortiert sind.

z.B. ID Nachname
1 Meier
2 Schulze
3 Schmidt

Wenn ich nun alle Zeilen selektiere und mittels

For Each Row as DatagridViewRow in MyDataGridView.SelectedRows
...
Next

die selektierten Zeilen durchlaufe, wird immer mit der letzten bzw.
untersten selektierten Zeile angefangen (im o.g. Beispiel kommt immer ID 3
Nachname Schmidt zuerst, dann ID 2 usw.).

Wie kann ich dafür sorgen, das beim Durchlaufen der SelectedRows immer OBEN
(bei ID 1) begonnen wird?

Bin für jeden Tip dankbar...
Peter Fleischer
2007-05-31 16:06:43 UTC
Permalink
Post by Heiko Röbke
Wenn ich nun alle Zeilen selektiere und mittels
For Each Row as DatagridViewRow in MyDataGridView.SelectedRows
...
Next
die selektierten Zeilen durchlaufe, wird immer mit der letzten bzw.
untersten selektierten Zeile angefangen (im o.g. Beispiel kommt immer
ID 3 Nachname Schmidt zuerst, dann ID 2 usw.).
Wie kann ich dafür sorgen, das beim Durchlaufen der SelectedRows
immer OBEN (bei ID 1) begonnen wird?
Hi Heiko,
das geht mit einer foreach-Schleife nicht. Bei foreach musst du immer davon
ausgehen, dass dir alle Elemente der Menge in zufälliger Reihenfolge
übergeben werden. Wenn du eine bestimmte Reihenfolge haben möchtest, musst
du mit Index arbeiten. In deinem Fall bedeutet das das Durchlaufen aller
Zeilen mit Prüfung auf "selektiert" in der von dir gewünschten Reihenfolge.
--
Viele Grüße

Peter
Heiko Röbke
2007-06-01 08:53:07 UTC
Permalink
Hallo Peter,

hättest Du evtl. zu Deinem Ansatz ein paar Beispielcodezeilen parat, um mir
diesbzgl. auf die Sprünge zu helfen? .Net ist noch zu neu für mich...

Danke schonmal...
Post by Peter Fleischer
Post by Heiko Röbke
Wenn ich nun alle Zeilen selektiere und mittels
For Each Row as DatagridViewRow in MyDataGridView.SelectedRows
...
Next
die selektierten Zeilen durchlaufe, wird immer mit der letzten bzw.
untersten selektierten Zeile angefangen (im o.g. Beispiel kommt immer
ID 3 Nachname Schmidt zuerst, dann ID 2 usw.).
Wie kann ich dafür sorgen, das beim Durchlaufen der SelectedRows
immer OBEN (bei ID 1) begonnen wird?
Hi Heiko,
das geht mit einer foreach-Schleife nicht. Bei foreach musst du immer
davon ausgehen, dass dir alle Elemente der Menge in zufälliger Reihenfolge
übergeben werden. Wenn du eine bestimmte Reihenfolge haben möchtest, musst
du mit Index arbeiten. In deinem Fall bedeutet das das Durchlaufen aller
Zeilen mit Prüfung auf "selektiert" in der von dir gewünschten Reihenfolge.
--
Viele Grüße
Peter
Peter Fleischer
2007-06-01 12:46:40 UTC
Permalink
Post by Heiko Röbke
hättest Du evtl. zu Deinem Ansatz ein paar Beispielcodezeilen parat,
um mir diesbzgl. auf die Sprünge zu helfen? .Net ist noch zu neu für
mich...
Hi Heiko,
hier mal ein Beispiel:

Public Class Form1

Dim bs1 As BindingSource
Dim dgv As New DataGridView

Private Sub Form1_Load_1(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles MyBase.Load
bs1 = New BindingSource(LoadDataSet, "Tab1")
'
With dgv
.Dock = DockStyle.Fill
.DataSource = bs1
Me.Controls.Add(dgv)
End With
'
Dim btn As New Button
With btn
.Text = "Show Selected"
.Dock = DockStyle.Top
AddHandler .Click, AddressOf btnClick
Me.Controls.Add(btn)
End With
'
End Sub

Private Sub btnClick(ByVal seneder As Object, ByVal e As EventArgs)
For i As Integer = 0 To bs1.Count - 1
If dgv.Rows(i).Selected Then
Trace.WriteLine(bs1.Item(i)(1).ToString)
End If
Next
End Sub

Private Function LoadDataSet() As DataSet
Dim ds As New DataSet
Dim dt1 As New DataTable("Tab1")
ds.Tables.Add(dt1)
With dt1
.Columns.Add(New DataColumn("ID", GetType(Guid)))
.Columns.Add(New DataColumn("col1", GetType(String)))
For i As Integer = 0 To 100
Dim r1 As DataRow = .NewRow
With r1
.Item(0) = Guid.NewGuid
.Item(1) = i.ToString & ". Row"
End With
.Rows.Add(r1)
Next
.AcceptChanges()
End With
Return ds
End Function

End Class
--
Viele Grüße

Peter
Peter Götz
2007-05-31 18:26:39 UTC
Permalink
Hallo Heiko,
Post by Heiko Röbke
Wie kann ich dafür sorgen, das beim Durchlaufen
der SelectedRows immer OBEN (bei ID 1)
begonnen wird?
Schau Dir mal das nachfolgende Beispiel an.
Mit dem Button "ShowSelRows" bekommst Du immer
eine Liste der selektierten Zeilen in genau der Sortierung
die Du auch im DataGridView (z.B. durch Mausklick
auf einen ColumnHeader) eingestellt hast.

' /// Code in einer leeren Form
Public Class Form1
Private mDV As DataView
Private mDT As DataTable
Private WithEvents DGV As DataGridView
Private WithEvents Button1 As Button

Private Sub Form1_Load _
( _
ByVal sender As System.Object, _
ByVal e As System.EventArgs _
) Handles MyBase.Load

Me.Size = New Size(250, 400)
CreateControls()
CreateData()
DGV.DataSource = mDV
DGV.AutoResizeColumns()
End Sub

Private Sub CreateControls()
DGV = New DataGridView
With DGV
.Name = "DGV"
.DefaultCellStyle.Font = New Font("Arial", 12)

.ColumnHeadersDefaultCellStyle.Font = _
New Font("Arial", 8, FontStyle.Bold)

.SetBounds _
( _
10, 10, _
Me.ClientSize.Width - 20, _
Me.ClientSize.Height - 70 _
)

.Anchor = AnchorStyles.Left Or _
AnchorStyles.Top Or _
AnchorStyles.Right Or _
AnchorStyles.Bottom
End With
Me.Controls.Add(DGV)

Button1 = New Button
With Button1
.Name = "Butto1"
.Text = "ShowSelRows"
.SetBounds _
( _
Me.ClientSize.Width - 110, _
Me.ClientSize.Height - 50, _
100, 40 _
)

.Anchor = AnchorStyles.Right Or _
AnchorStyles.Bottom
End With
Me.Controls.Add(Button1)
End Sub

Private Sub CreateData()
Dim i As Integer
Dim DR As DataRow

mDT = New DataTable
With mDT
.Columns.Add("ID", GetType(Integer))
.Columns.Add("Text", GetType(String))

For i = 1 To 12
DR = .NewRow
DR.Item(0) = i
DR.Item(1) = MonthName(i)
.Rows.Add(DR)
Next
.AcceptChanges()
End With
mDV = New DataView(mDT)
mDV.Sort = "ID"
End Sub

Private Sub DGV_SelectionChanged _
( _
ByVal sender As Object, _
ByVal e As System.EventArgs _
) Handles DGV.SelectionChanged

Button1.Enabled = (DGV.SelectedRows.Count > 0)
End Sub

Private Sub Button1_Click _
( _
ByVal sender As Object, _
ByVal e As System.EventArgs _
) Handles Button1.Click

Dim DV As DataView
Dim DT As DataTable = mDT.Clone
Dim DR As DataRow

For Each R As DataGridViewRow In DGV.SelectedRows
With DT
DR = .NewRow
DR.ItemArray = _
DirectCast(R.DataBoundItem, _
DataRowView).Row.ItemArray

.Rows.Add(DR)
End With
Next
DV = New DataView(DT)
DV.Sort = mDV.Sort

Dim strBuffer As String = ""

For Each DRV As DataRowView In DV
strBuffer = strBuffer & DRV.Item(0).ToString & _
" : " & _
DRV.Item(1).ToString & ControlChars.CrLf
Next
MsgBox(strBuffer, MsgBoxStyle.Information)
End Sub
End Class
' \\\ E N T E

Gruß aus St.Georgen
Peter Götz
www.gssg.de (mit VB-Tipps u. Beispielprogrammen)
Heiko Röbke
2007-06-01 09:00:35 UTC
Permalink
Hallo Peter,

vielen Dank für Deinen Ansatz. Geht auch soweit, aber: So kann ich beim
Durchlaufen der SelectedRows nicht mehr direkt auf ein DataGridviewRow
(DGVR.cells("ID").value) referenzieren, sondern muss auf ein DataRowView
(DRV.Item(0).tostring). Leider ist es wichtig für mich, beim Iterieren die
Spalten beim Namen zu nennen, anstatt per Spaltenindex. So müsste ich zuviel
an der bestehenden Anwendung ändern.

Viele Grüsse...
Post by Peter Götz
Hallo Heiko,
Post by Heiko Röbke
Wie kann ich dafür sorgen, das beim Durchlaufen
der SelectedRows immer OBEN (bei ID 1)
begonnen wird?
Schau Dir mal das nachfolgende Beispiel an.
Mit dem Button "ShowSelRows" bekommst Du immer
eine Liste der selektierten Zeilen in genau der Sortierung
die Du auch im DataGridView (z.B. durch Mausklick
auf einen ColumnHeader) eingestellt hast.
' /// Code in einer leeren Form
Public Class Form1
Private mDV As DataView
Private mDT As DataTable
Private WithEvents DGV As DataGridView
Private WithEvents Button1 As Button
Private Sub Form1_Load _
( _
ByVal sender As System.Object, _
ByVal e As System.EventArgs _
) Handles MyBase.Load
Me.Size = New Size(250, 400)
CreateControls()
CreateData()
DGV.DataSource = mDV
DGV.AutoResizeColumns()
End Sub
Private Sub CreateControls()
DGV = New DataGridView
With DGV
.Name = "DGV"
.DefaultCellStyle.Font = New Font("Arial", 12)
.ColumnHeadersDefaultCellStyle.Font = _
New Font("Arial", 8, FontStyle.Bold)
.SetBounds _
( _
10, 10, _
Me.ClientSize.Width - 20, _
Me.ClientSize.Height - 70 _
)
.Anchor = AnchorStyles.Left Or _
AnchorStyles.Top Or _
AnchorStyles.Right Or _
AnchorStyles.Bottom
End With
Me.Controls.Add(DGV)
Button1 = New Button
With Button1
.Name = "Butto1"
.Text = "ShowSelRows"
.SetBounds _
( _
Me.ClientSize.Width - 110, _
Me.ClientSize.Height - 50, _
100, 40 _
)
.Anchor = AnchorStyles.Right Or _
AnchorStyles.Bottom
End With
Me.Controls.Add(Button1)
End Sub
Private Sub CreateData()
Dim i As Integer
Dim DR As DataRow
mDT = New DataTable
With mDT
.Columns.Add("ID", GetType(Integer))
.Columns.Add("Text", GetType(String))
For i = 1 To 12
DR = .NewRow
DR.Item(0) = i
DR.Item(1) = MonthName(i)
.Rows.Add(DR)
Next
.AcceptChanges()
End With
mDV = New DataView(mDT)
mDV.Sort = "ID"
End Sub
Private Sub DGV_SelectionChanged _
( _
ByVal sender As Object, _
ByVal e As System.EventArgs _
) Handles DGV.SelectionChanged
Button1.Enabled = (DGV.SelectedRows.Count > 0)
End Sub
Private Sub Button1_Click _
( _
ByVal sender As Object, _
ByVal e As System.EventArgs _
) Handles Button1.Click
Dim DV As DataView
Dim DT As DataTable = mDT.Clone
Dim DR As DataRow
For Each R As DataGridViewRow In DGV.SelectedRows
With DT
DR = .NewRow
DR.ItemArray = _
DirectCast(R.DataBoundItem, _
DataRowView).Row.ItemArray
.Rows.Add(DR)
End With
Next
DV = New DataView(DT)
DV.Sort = mDV.Sort
Dim strBuffer As String = ""
For Each DRV As DataRowView In DV
strBuffer = strBuffer & DRV.Item(0).ToString & _
" : " & _
DRV.Item(1).ToString & ControlChars.CrLf
Next
MsgBox(strBuffer, MsgBoxStyle.Information)
End Sub
End Class
' \\\ E N T E
Gruß aus St.Georgen
Peter Götz
www.gssg.de (mit VB-Tipps u. Beispielprogrammen)
Peter Götz
2007-06-01 13:03:37 UTC
Permalink
Hallo Heiko,
So kann ich beim Durchlaufen der SelectedRows nicht
mehr direkt auf ein DataGridviewRow
(DGVR.cells("ID").value) referenzieren, sondern muss
auf ein DataRowView (DRV.Item(0).tostring). Leider ist
es wichtig für mich, beim Iterieren die Spalten beim
Namen zu nennen, anstatt per Spaltenindex. So müsste
ich zuviel an der bestehenden Anwendung ändern.
Dann musst Du eben doch die gesamte DataGridView.Rows
durchlaufen und bei jeder Zeile auf Selected prüfen, so wie im
nachfolgenden Beispiel:

' /// Code in einer leeren Form1
Imports System.Text
Public Class Form1
Private WithEvents DGV As DataGridView
Private WithEvents Button1 As Button
Private mDT As DataTable
Private mDV As DataView

Private Sub Form1_Load _
( _
ByVal sender As Object, _
ByVal e As System.EventArgs _
) Handles Me.Load

CreateControls()
CreateData()
DGV.DataSource = mDV
DGV.AutoResizeColumns()
End Sub

Private Sub CreateControls()
DGV = New DataGridView
With DGV
.Name = "DGV"
.SetBounds( _
10, 10, _
Me.ClientSize.Width - 20, _
Me.ClientSize.Height - 70 _
)

.Anchor = AnchorStyles.Left Or _
AnchorStyles.Top Or _
AnchorStyles.Right Or _
AnchorStyles.Bottom

.DefaultCellStyle.Font = New Font("Arial", 12)

.ColumnHeadersDefaultCellStyle.Font = _
New Font("Arial", 8, FontStyle.Bold)
End With
Me.Controls.Add(DGV)

Button1 = New Button
With Button1
.Name = "Button1"
.Text = "ShowSelRows"
.Font = New Font("Arial", 9, FontStyle.Bold)

.SetBounds( _
Me.ClientSize.Width - 120, _
Me.ClientSize.Height - 50, _
110, 40 _
)

.Anchor = AnchorStyles.Right Or _
AnchorStyles.Bottom
End With

Me.Controls.Add(Button1)
End Sub

Private Sub CreateData()
Dim i As Integer
Dim DR As DataRow

mDT = New DataTable
With mDT
.Columns.Add("ID", GetType(Integer))
.Columns.Add("Text", GetType(String))
For i = 1 To 12
DR = .NewRow
DR.Item(0) = i
DR.Item(1) = MonthName(i)
.Rows.Add(DR)
Next
.AcceptChanges()
End With
mDV = New DataView(mDT)

End Sub

Private Sub Button1_Click _
( _
ByVal sender As Object, _
ByVal e As System.EventArgs _
) Handles Button1.Click

Dim i As Integer
Dim strBuffer As String
Dim cID As DataGridViewCell
Dim cText As DataGridViewCell
Dim SB As StringBuilder

Dim DRV As DataRowView
Dim DR As DataRow


If DGV.SelectedRows.Count > 0 Then
SB = New StringBuilder
SB.AppendLine("Selected Rows")

For i = 0 To DGV.Rows.Count - 1
If DGV.Rows(i).Selected Then
strBuffer = ""

' Zugriffgriff auf DataRowView u. DataRow
DRV = DirectCast _
(DGV.Rows(i).DataBoundItem, DataRowView)

If DRV IsNot Nothing Then
DR = DRV.Row
strBuffer = DR.Item(0).ToString & _
" : " & _
DR.Item(1).ToString()

Console.WriteLine(strBuffer)
End If

' Zugriff auf DataGridViewRows/Cells
cID = DGV.Rows(i).Cells(0)
cText = DGV.Rows(i).Cells(1)
If cID.Value IsNot Nothing Then
SB.Append(cID.Value.ToString)
Else
SB.Append("_")
End If
SB.Append(" : ")
If cText.Value IsNot Nothing Then
SB.AppendLine(cText.Value.ToString)
Else
SB.AppendLine("_")
End If
End If
Next
MsgBox(SB.ToString, MsgBoxStyle.Information)
End If
End Sub
End Class
' \\\ E N T E

Gruß aus St.Georgen
Peter Götz
www.gssg.de (mit VB-Tipps u. Beispielprogrammen)
Heiko Röbke
2007-06-02 11:16:03 UTC
Permalink
Hallo Peter,

wenn ich mein DataGridView so wie in Deinem Beispiel

For i = 0 To DGV.Rows.Count - 1
If DGV.Rows(i).Selected Then
...
EndIf
Next

durchlaufe, wird scheinbar immer von oben nach unten iteriert. So wie ich es
brauche :-)

Oder war das bei meinen Test nur Zufall und die o.g. For-Schleife geht wie
eine ForEach-Schleife immer in zufälliger Reihenfolge über die Menge?
Post by Peter Götz
Hallo Heiko,
So kann ich beim Durchlaufen der SelectedRows nicht
mehr direkt auf ein DataGridviewRow
(DGVR.cells("ID").value) referenzieren, sondern muss
auf ein DataRowView (DRV.Item(0).tostring). Leider ist
es wichtig für mich, beim Iterieren die Spalten beim
Namen zu nennen, anstatt per Spaltenindex. So müsste
ich zuviel an der bestehenden Anwendung ändern.
Dann musst Du eben doch die gesamte DataGridView.Rows
durchlaufen und bei jeder Zeile auf Selected prüfen, so wie im
' /// Code in einer leeren Form1
Imports System.Text
Public Class Form1
Private WithEvents DGV As DataGridView
Private WithEvents Button1 As Button
Private mDT As DataTable
Private mDV As DataView
Private Sub Form1_Load _
( _
ByVal sender As Object, _
ByVal e As System.EventArgs _
) Handles Me.Load
CreateControls()
CreateData()
DGV.DataSource = mDV
DGV.AutoResizeColumns()
End Sub
Private Sub CreateControls()
DGV = New DataGridView
With DGV
.Name = "DGV"
.SetBounds( _
10, 10, _
Me.ClientSize.Width - 20, _
Me.ClientSize.Height - 70 _
)
.Anchor = AnchorStyles.Left Or _
AnchorStyles.Top Or _
AnchorStyles.Right Or _
AnchorStyles.Bottom
.DefaultCellStyle.Font = New Font("Arial", 12)
.ColumnHeadersDefaultCellStyle.Font = _
New Font("Arial", 8, FontStyle.Bold)
End With
Me.Controls.Add(DGV)
Button1 = New Button
With Button1
.Name = "Button1"
.Text = "ShowSelRows"
.Font = New Font("Arial", 9, FontStyle.Bold)
.SetBounds( _
Me.ClientSize.Width - 120, _
Me.ClientSize.Height - 50, _
110, 40 _
)
.Anchor = AnchorStyles.Right Or _
AnchorStyles.Bottom
End With
Me.Controls.Add(Button1)
End Sub
Private Sub CreateData()
Dim i As Integer
Dim DR As DataRow
mDT = New DataTable
With mDT
.Columns.Add("ID", GetType(Integer))
.Columns.Add("Text", GetType(String))
For i = 1 To 12
DR = .NewRow
DR.Item(0) = i
DR.Item(1) = MonthName(i)
.Rows.Add(DR)
Next
.AcceptChanges()
End With
mDV = New DataView(mDT)
End Sub
Private Sub Button1_Click _
( _
ByVal sender As Object, _
ByVal e As System.EventArgs _
) Handles Button1.Click
Dim i As Integer
Dim strBuffer As String
Dim cID As DataGridViewCell
Dim cText As DataGridViewCell
Dim SB As StringBuilder
Dim DRV As DataRowView
Dim DR As DataRow
If DGV.SelectedRows.Count > 0 Then
SB = New StringBuilder
SB.AppendLine("Selected Rows")
For i = 0 To DGV.Rows.Count - 1
If DGV.Rows(i).Selected Then
strBuffer = ""
' Zugriffgriff auf DataRowView u. DataRow
DRV = DirectCast _
(DGV.Rows(i).DataBoundItem, DataRowView)
If DRV IsNot Nothing Then
DR = DRV.Row
strBuffer = DR.Item(0).ToString & _
" : " & _
DR.Item(1).ToString()
Console.WriteLine(strBuffer)
End If
' Zugriff auf DataGridViewRows/Cells
cID = DGV.Rows(i).Cells(0)
cText = DGV.Rows(i).Cells(1)
If cID.Value IsNot Nothing Then
SB.Append(cID.Value.ToString)
Else
SB.Append("_")
End If
SB.Append(" : ")
If cText.Value IsNot Nothing Then
SB.AppendLine(cText.Value.ToString)
Else
SB.AppendLine("_")
End If
End If
Next
MsgBox(SB.ToString, MsgBoxStyle.Information)
End If
End Sub
End Class
' \\\ E N T E
Gruß aus St.Georgen
Peter Götz
www.gssg.de (mit VB-Tipps u. Beispielprogrammen)
Peter Götz
2007-06-02 11:33:27 UTC
Permalink
Hallo Heiko,
Post by Heiko Röbke
wenn ich mein DataGridView so wie in Deinem Beispiel
For i = 0 To DGV.Rows.Count - 1
If DGV.Rows(i).Selected Then
...
EndIf
Next
durchlaufe, wird scheinbar immer von oben nach unten
iteriert. So wie ich es brauche :-)
Nicht nur scheinbar, sondern tatsächlich genau so.
Ist doch auch logisch:

DGV.Rows(0)

ist nun mal die oberste Zeile im Grid, dann kommt

DGV.Rows(1)

usw. Nachdem die Schleife von 0 bis DGV.Rows.Count-1
läuft werden eben alle Zeilen von der ersten (oben) bis zur
letzten (ganz unten) durchlaufen.
Post by Heiko Röbke
Oder war das bei meinen Test nur Zufall und die o.g.
For-Schleife geht wie eine ForEach-Schleife immer
in zufälliger Reihenfolge über die Menge?
Hmm?
Wo sollte denn da ein Zufall ins Spiel kommen?
Von 0 bis DGV.Rows.Count-1 gibt es keine Zufälle.
Die For-Next-Schleife zählt immer schön der Reihe
nach jeweils um 1 weiter und damit also jeweils zur
nächsten Zeile.

Du könntest Deine Schleife auch umdrehen:

For i = DGV.Rows.Count - 1 to 0 Step -1
If DGV.Rows(i).Selected Then
...
EndIf
Next

Jetzt würde von der letzten (untersten) Zeile bis zur
ersten (obersten) gelaufen. Auch das nicht zufällig,
sondern eben ganz genau so.


Gruß aus St.Georgen
Peter Götz
www.gssg.de (mit VB-Tipps u. Beispielprogrammen)

Loading...