set new DataGridViewComboBoxCell for after validation of previous DataGridViewTextBoxCell in same column and row - vb.net

I have this DataGridView and it has a DataGridViewTextBoxColumn where user can type a number, and after he types it I perform a search to find previous records under that number.
I want to show this records so user can select one of them, or keep the value typed, which means he wants to create a new record.
For that, I want to replace each DataGridViewTextBoxCell for a DataGridViewComboBoxCell with those options when user has finished typing.
However, it is raising this exception when I try to permform this replacement: "System.InvalidOperationException" in System.Windows.Forms.dll. Additional information: The operation is not valid because it results in a reentering call to the function SetCurrentCellAddressCore.
Here is my code (I already tried to handle CellLeave instead of CellValidated):
Private Sub DataGridViewDebitos_CellValidated(sender As Object, e As DataGridViewCellEventArgs) Handles DataGridViewDebitos.CellValidated
DataGridViewDebitos.EndEdit(DataGridViewDataErrorContexts.Commit)
If e.ColumnIndex = ColumnDebito.Index Then
Dim cellDebito = DataGridViewDebitos.Rows(e.RowIndex).Cells(ColumnDebito.Index)
Dim numDebito = String.Concat(If(cellDebito.Value, "").ToString.Where(Function(c) Char.IsLetterOrDigit(c)))
If TypeOf cellDebito Is DataGridViewTextBoxCell AndAlso numDebito.Length >= 3 Then
Dim prcsa As New List(Of JObject) 'In real version, prcsa is populated by an external function, but it doesn't affect the result
Dim j = New JObject
j.SetProperty("id", 0)
j.SetProperty("nome", cellDebito.Value)
prcsa.Insert(0, j) 'This option is always present, it allows user to keep simply what was typed
'Exception hapens here
DataGridViewDebitos(cellDebito.ColumnIndex, cellDebito.RowIndex) =
New DataGridViewComboBoxCell With {
.DataSource = prcsa,
.DisplayMember = "nome",
.FlatStyle = FlatStyle.Flat,
.ValueMember = "id",
.Value = prcsa(0).Value(Of Integer)("id")}
End If
End If
End Sub
Thank you very much

I've put the problematic statement into a Lambda sub via .BeginInvoke and this solved the problem, only I don't know why...
Can anyone explain the mechanics of it to me? Thank you!
Private Sub DataGridViewDebitos_CellValidated(sender As Object, e As DataGridViewCellEventArgs) Handles DataGridViewDebitos.CellValidated
DataGridViewDebitos.EndEdit(DataGridViewDataErrorContexts.Commit)
If e.ColumnIndex = ColumnDebito.Index Then
Dim cellDebito = DataGridViewDebitos.Rows(e.RowIndex).Cells(ColumnDebito.Index)
Dim numDebito = String.Concat(If(cellDebito.Value, "").ToString.Where(Function(c) Char.IsLetterOrDigit(c)))
If TypeOf cellDebito Is DataGridViewTextBoxCell AndAlso numDebito.Length >= 3 Then
Dim prcsa As New List(Of JObject) 'In real version, prcsa is populated by an external function, but it doesn't affect the result
Dim j = New JObject
j.SetProperty("id", 0)
j.SetProperty("nome", cellDebito.Value)
prcsa.Insert(0, j) 'This option is always present, it allows user to keep simply what was typed
'Exception hapens here
DataGridViewDebitos.BeginInvoke(
Sub()
DataGridViewDebitos(cellDebito.ColumnIndex, cellDebito.RowIndex) =
New DataGridViewComboBoxCell With {
.DataSource = prcsa,
.DisplayMember = "nome",
.FlatStyle = FlatStyle.Flat,
.ValueMember = "id",
.Value = prcsa(0)("id")}
End Sub)
End If
End If
End Sub

Related

Pasting new records to a Bound DataGridView

Apologies if this has already been asked. If so, I am unable to find a simple solution. I am trying to allow a user to copy/paste multiple records in a DataGridView (the in memory copy of the data, to be saved later when the user clicks the save button) and cannot find anything that works. It probably is because there is something I do not understand about all of this.
I set up a standard edit form with Visual Studio's drag/table into a form, so it's using a BindingSource control and all the other controls that come with doing that. It works just fine when manually entering something in the new row one by one, so it seems to be set up correctly, but when it comes to adding a record (or multiples) using code, nothing seems to work.
I tried a few things as outline in the code below. Could someone please at least steer me in the right direction? It cannot be that difficult to paste multiple records.
I run this when the user presses Control-V (the clipboard correctly holds the delimited strings):
Private Sub PasteClipboard()
If Clipboard.ContainsText Then
Dim sLines() As String = Clipboard.GetText.Split(vbCrLf)
For Each sLine As String In sLines
Dim Items() As String = sLine.Split(vbTab)
Dim drv As DataRowView = AdjustmentsBindingSource.AddNew()
drv.Item(1) = Items(0)
drv.Item(2) = Items(1)
drv.Item(3) = Items(2)
drv.Item(4) = Items(3)
'Error on next line : Cannot add external objects to this list.
AdjustmentsBindingSource.Add(drv)
Next
End If
End Sub
EDIT
(the bindingsource is bound to a dataadapter, which is bound to a table in an mdb file, if that helps understand)
I adjusted the inner part of the code to this:
If (RowHasData(Items)) Then
Dim drv As DataRowView = AdjustmentsBindingSource.AddNew()
drv.Item("FontName") = Items(0)
drv.Item("FontSize") = Items(1)
drv.Item("LetterCombo") = Items(2)
drv.Item("Adjustment") = Items(3)
drv.Item("HorV") = Items(4)
End If
It kinda works, but it also adds a blank row before the 2 new rows. Not sure where that is coming from, as I have even included your RowHasData() routine...
I would think that “attemp3” SHOULD work, however, it is unclear “what” the AdjustmentsBindingSource’s DataSource is. Is it a List<T> or DataTable?
If I set the BinngSource.DataSource to a DataTable, then attempt 3 appears to work. Below is an example that worked.
Private Sub PasteClipboard2()
If Clipboard.ContainsText Then
Dim sLines() As String = Clipboard.GetText.Split(vbCrLf)
For Each sLine As String In sLines
Dim Items() As String = sLine.Split(vbTab)
If (RowHasData(Items)) Then
Dim drv As DataRowView = AdjustmentsBindingSource.AddNew()
drv.Item("FontName") = Items(0)
drv.Item("FontSize") = Items(1)
drv.Item("LetterCombo") = Items(2)
drv.Item("Adjustment") = Items(3)
drv.Item("HorV") = Items(4)
End If
Next
End If
End Sub
This appears to work in my tests. I added a small function (RowHasData) to avoid malformed strings causing problems. It simply checks the size (at least 5 items) and also checks to make sure a row actually has “some” data. If a row is just empty strings, then it is ignored.
Private Function RowHasData(items As String())
If (items.Count >= 5) Then
For Each item In items
If (item <> "") Then Return True
Next
End If
Return False
End Function
I am guessing it would be just as easy to add the new rows “directly” to the BindingSource’s DataSource. In the example below, the code is adding the row “directly” to the DataTable that is used as a DataSource to the BindingSource. I am confident you could do the same thing with a List<T> by simply adding a new object to the list. Below is a complete example using a BindingSource and a DataTable. This simply adds the rows to the bottom of the table.
Dim gridTable1 As DataTable
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
PasteClipboard()
End Sub
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
gridTable1 = GetTable()
FillTable(gridTable1)
AdjustmentsBindingSource.DataSource = gridTable1
AdjustmentsDataGridView.DataSource = AdjustmentsBindingSource
End Sub
Private Function GetTable() As DataTable
Dim dt = New DataTable()
dt.Columns.Add("FontName", GetType(String))
dt.Columns.Add("FontSize", GetType(String))
dt.Columns.Add("LetterCombo", GetType(String))
dt.Columns.Add("Adjustment", GetType(String))
dt.Columns.Add("HorV", GetType(String))
Return dt
End Function
Private Sub FillTable(dt As DataTable)
For index = 1 To 10
dt.Rows.Add("Name_" + index.ToString(), "Size_" + index.ToString(), "Combo_" + index.ToString(), "Adjust_" + index.ToString(), "HorV_" + index.ToString())
Next
End Sub
Private Sub PasteClipboard()
If Clipboard.ContainsText Then
Dim sLines() As String = Clipboard.GetText.Split(vbCrLf)
Try
Dim dataRow As DataRow
For Each sLine As String In sLines
Dim Items() As String = sLine.Split(vbTab)
If (RowHasData(Items)) Then
dataRow = gridTable1.NewRow()
dataRow("FontName") = Items(0)
dataRow("FontSize") = Items(1)
dataRow("LetterCombo") = Items(2)
dataRow("Adjustment") = Items(3)
dataRow("HorV") = Items(4)
gridTable1.Rows.Add(dataRow)
End If
Next
Catch ex As Exception
MessageBox.Show("Error: " + ex.Message)
End Try
End If
End Sub
Private Function RowHasData(items As String())
If (items.Count >= 5) Then
For Each item In items
If (item <> "") Then Return True
Next
End If
Return False
End Function
Hope the code helps…
Last but important, I am only guessing that you may have not “TESTED” the different ways users can “SELECT” data and “how” other applications “copy” that selected data. My previous tests using the WIN-OS “Clipboard” can sometimes give unexpected results. Example, if the user selects multiple items using the ”Ctrl” key to “ADD” to the selection, extra rows appeared in the Clipboard if the selection was not contiguous. My important point is that using the OS clipboard is quirky IMHO. I recommend LOTS of testing on the “different” ways the user can select the data. If this is not an issue then the code above should work.

Multi-Threading IP Address Pings Causing Application Crash

Boy, learning something new can be a real headache if you can't find a solid source. I have been designing applications in a linear fashion for some time now and want to step up into a more powerful approach. I have been reading up on threading, and perhaps have gone to an larger level than I should. However, one usually steps up when the application calls for it and no better time than the present to learn something new.
My program is designed to do something that seems rather simple, but has become extremely difficult to create in a smooth running manor. The original design created object of each device on the network it wished to ping, in my real world environment they are Kindles. The goal was to ensure they were still connected to the network by Pining them. I used a For Loop and Obj Array to do this set on a Timer. This had unexpected results causing the ListView to flicker and load slowly after the ListView1.Items.Clear. I evolved into updating the List Items rather than clearing them and the flicker remained.
I assumed this was due to the slow process of the array and pings so I started hunting for solutions and came across Multi-Threading. I have known about this for some time, but have yet to dive into the practice. My program seemed to need more speed and smoother operation so I took a stab at it. The below code in its complete form is the result, however it crashes and throws errors. Clearly I have not used Threading as it was intended. Using it in simpler functions works fine and I feel I have the grasp. That is if i want my program to pointlessly run counters.
I don't know what to do next in my steps for getting this task done, and figure I am combining several different methods into a mush of dead program. I could really use some help getting back on track with this. All comments welcome and thank you for checking out my code.
Form1 Code
Public Class Form1
'Obj Array
Public Shared objDevice As New List(Of kDevice)
'Thread Array for each Obj
Public Shared thread() As System.Threading.Thread
Private Sub ipRefresh(objID, itemPos)
Dim objDev As kDevice = objID
If My.Computer.Network.Ping(objDev.kIP) Then
objDev.kStatus = "Online"
objDev.kPings = 0
Else
objDev.kPings += 1
End If
If objDev.kPings >= 8 Then
objDev.kStatus = "Offline"
objDev.kPings = 0
ListView1.Items(itemPos).BackColor = Color.Red
End If
Dim str(4) As String
Dim itm As ListViewItem
str(0) = objDev.kName
str(1) = objDev.kIP
str(2) = objDev.kStatus
str(3) = objDev.kPings
itm = New ListViewItem(str)
ListView1.Items(itemPos) = itm
End Sub
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Me.CheckForIllegalCrossThreadCalls = False
' Adding ListView Columns
ListView1.Columns.Add("Device", 100, HorizontalAlignment.Left)
ListView1.Columns.Add("IP Address", 150, HorizontalAlignment.Left)
ListView1.Columns.Add("Status", 60, HorizontalAlignment.Left)
ListView1.Columns.Add("Pings", 60, HorizontalAlignment.Left)
Dim ipList As New List(Of String)
Dim nameList As New List(Of String)
Using MyReader As New Microsoft.VisualBasic.FileIO.TextFieldParser("kDevices.csv")
MyReader.TextFieldType = Microsoft.VisualBasic.FileIO.FieldType.Delimited
MyReader.Delimiters = New String() {","}
Dim currentRow As String()
Dim rowP As Integer = 1
While Not MyReader.EndOfData
Try
currentRow = MyReader.ReadFields()
Dim cellP As Integer = 0
Dim nTemp As String = ""
For Each currentField As String In currentRow
Select Case cellP
Case 0
nameList.Add(currentField.Replace("""", ""))
Case 1
ipList.Add(currentField.Replace("""", ""))
End Select
cellP += 1
Next
Catch ex As Microsoft.VisualBasic.FileIO.MalformedLineException
MsgBox("Line " & ex.Message & " is invalid. Skipping")
End Try
rowP += 1
End While
End Using
Dim nameLAR As String() = nameList.ToArray
Dim ipLAR As String() = ipList.ToArray
ReDim Preserve thread(nameLAR.Length)
For i As Integer = 0 To nameLAR.Length - 1
Dim newDevice As New kDevice
Dim objNum = i
objDevice.Add(newDevice)
newDevice.kName = nameLAR(i)
newDevice.kIP = ipLAR(i)
If My.Computer.Network.Ping(newDevice.kIP) Then
newDevice.kStatus = "Online"
Else
newDevice.kStatus = "Loading"
End If
Dim str(4) As String
Dim itm As ListViewItem
str(0) = newDevice.kName
str(1) = newDevice.kIP
str(2) = newDevice.kStatus
str(3) = newDevice.kPings
itm = New ListViewItem(str)
If newDevice.kStatus = "Loading" Then
itm.BackColor = Color.Yellow
End If
ListView1.Items.Add(itm)
thread(objNum) = New System.Threading.Thread(Sub() Me.ipRefresh(objDevice(objNum), objNum))
Next
End Sub
Private Sub Timer1_Tick(sender As Object, e As EventArgs) Handles Timer1.Tick
For i As Integer = 0 To objDevice.Count - 1
thread(i).Start()
Next
End Sub
End Class
kDevice Class
Public Class kDevice
Private strkName As String
Private strkIP As String
Private strkStatus As String
Private strkLastStatus As String
Private strkPings As Integer = 0
Public Property kName As String
Get
Return strkName
End Get
Set(value As String)
strkName = value
End Set
End Property
Public Property kIP As String
Get
Return strkIP
End Get
Set(value As String)
strkIP = value
End Set
End Property
Public Property kStatus As String
Get
Return strkStatus
End Get
Set(value As String)
strkStatus = value
End Set
End Property
Public Property kPings As Integer
Get
Return strkPings
End Get
Set(value As Integer)
strkPings = value
End Set
End Property
End Class
The Error / Crash on Line 32 of my code which is when it tries to pass the update to the ListView Item
An unhandled exception of type 'System.ArgumentException'
occurred in Microsoft.VisualBasic.dll
Additional information: InvalidArgument=Value of '18'
is not valid for 'index'.
or
An unhandled exception of type 'System.NullReferenceException'
occurred in Microsoft.VisualBasic.dll
Additional information: Object reference not set to an instance
of an object.
If my code does not make sense, or at lease the idea of what I was trying to make it do, please let me know and I will explain whichever parts are unclear. Again thank you for looking over my issue.
Just a possible issue I noticed:
Dim str(4) As String
Dim itm As ListViewItem
str(0) = newDevice.kName
str(1) = newDevice.kIP
str(2) = newDevice.kStatus
str(3) = newDevice.kPings
itm = New ListViewItem(str)
If newDevice.kStatus = "Loading" Then
itm.BackColor = Color.Yellow
End If
ListView1.Items.Add(itm)
In this bit here, you declare str(4) which would be 5 possible indexes (remember it starts at zero), where you should have 4 (str(3)) . I don't think this is the whole issue, but just a small bit you should probably fix. You also may want to look into other ways to update the listview without setting
Me.CheckForIllegalCrossThreadCalls = False
Here's an awesome guide that helped me when I did my first multi threaded application: http://checktechno.blogspot.com/2012/11/multi-thread-for-newbies.html

Merge two identical DataTables results in DataRowState.Modified

Am I wrong assuming that if two identical DataTables are merged the state of each row will be preserved?
Take a look at this simple example. It creates two identical tables and merge the updated table with original table. But the returned table in original.GetChanges() is not Nothing as expected. Also, the state of each row in the original table are changed to Modified.
So what am I missing? Do I really have to create my own merge method to achieve this?
Public Sub Test()
Dim original As DataTable = Me.CreateTableWithData()
Dim updated As DataTable = Me.CreateTableWithData()
Dim preserveChanges As Boolean = True
Dim msAction As MissingSchemaAction = MissingSchemaAction.Ignore
original.Merge(updated, preserveChanges, msAction)
Dim changes As DataTable = original.GetChanges()
MessageBox.Show(String.Format("Count={0}", If((changes Is Nothing), 0, changes.Rows.Count)), Me.Text, MessageBoxButtons.OK, MessageBoxIcon.Information)
If (Not changes Is Nothing) Then changes.Dispose() : changes = Nothing
updated.Dispose() : updated = Nothing
original.Dispose() : original = Nothing
End Sub
Private Function CreateTableWithData() As DataTable
Dim table As New DataTable("TEST")
table.Columns.Add("ID", GetType(Integer))
table.Columns.Add("VALUE", GetType(String))
table.PrimaryKey = New DataColumn() {table.Columns(0)}
table.Rows.Add(1, "Value 1")
table.Rows.Add(2, "Value 2")
table.AcceptChanges()
Return table
End Function
Output: Count=2
Edit - The workaround
The following code is a workaround for this strange(?) behavior.
Private Shared Sub Merge(target As DataTable, source As DataTable, preserveChanges As Boolean, msa As MissingSchemaAction)
target.Merge(source, preserveChanges, msa)
Dim row As DataRow
Dim column As DataColumn
Dim acceptChanges As Boolean
For Each row In target.Rows
If ((row.RowState = DataRowState.Modified) AndAlso ((row.HasVersion(DataRowVersion.Original)) AndAlso (row.HasVersion(DataRowVersion.Default)))) Then
acceptChanges = True
For Each column In target.Columns
If (Not Object.Equals(row.Item(column, DataRowVersion.Original), row.Item(column, DataRowVersion.Default))) Then
acceptChanges = False
Exit For
End If
Next
If (acceptChanges) Then
row.AcceptChanges()
End If
End If
Next
acceptChanges = Nothing
column = Nothing
row = Nothing
End Sub
After some time of working with DataTable merge I found the best solution to merging data, preserving changes and not setting the RowState to Modified for all of the existing rows.
What I discovered is that all of the rows in the original DataTable would have their RowState set to Modified if you use the DataTable Merge and pass True as the preserve changes property. If you pass false instead, the RowStates remain the same.
Going back to the documentation for the DataTable.Merge(DataTable, Boolean, MissingSchemaAction) Method I found this:
...In this scenario, the GetChanges method is first invoked. That method returns a second DataTable optimized for validating and merging. This second DataTable object contains only the DataTable and DataRow objects objects that were changed, resulting in a subset of the original DataTable...
From there I started to realize that the this merge is not really intended to be used with the original data directly... instead you should merge against the table returned by the GetChanges method (passing true in preserving changes) and then merge the changes table into the original source passing false for the preserving changes parameter.
To demonstrate this I have created the following class:
Class TableManger
Implements ComponentModel.INotifyPropertyChanged
Private _table1 As System.Data.DataTable
Private _table2 As System.Data.DataTable
Private _changesDetected As Integer = 0
Public ReadOnly Property Table1
Get
Return _table1
End Get
End Property
Public ReadOnly Property ChangesDetected As Integer
Get
Return _changesDetected
End Get
End Property
Public Sub New()
_table1 = CreateTableWithData()
_table1.AcceptChanges()
AddHandler _table1.RowChanged, New System.Data.DataRowChangeEventHandler(AddressOf Row_Changed)
End Sub
Public Sub MergeTables()
_table2 = _table1.Clone
Dim tableRows As New List(Of System.Data.DataRow)
For Each r In _table1.Rows
Dim dr2 = _table2.NewRow
For Each col As System.Data.DataColumn In _table1.Columns
dr2(col.ColumnName) = r(col.ColumnName)
Next
_table2.Rows.Add(dr2)
tableRows.Add(dr2)
Next
_table2.AcceptChanges()
If _table2.Rows.Count > 0 Then
_table2.Rows(0)(1) = "TB2 Changed"
End If
If _table1.Rows.Count > 0 Then
'_table1.Rows(0)(1) = "TB1 Change"'
_table1.Rows(1)(1) = "TB1 Change"
End If
_changesDetected = 0
Dim perserveChanges As Boolean = True
Dim msAction As System.Data.MissingSchemaAction = System.Data.MissingSchemaAction.Ignore
Dim changes As System.Data.DataTable = _table1.GetChanges()
If changes IsNot Nothing Then
changes.Merge(_table2, perserveChanges, msAction)
_table1.Merge(changes, False, msAction)
Else
_table1.Merge(_table2, False, msAction)
End If
MessageBox.Show(String.Format("Changes in Change Table: {0} {1}Changes Detected: {2}", If((changes Is Nothing), 0, changes.Rows.Count), System.Environment.NewLine, _changesDetected), "Testing")
RaiseEvent PropertyChanged(Me, New ComponentModel.PropertyChangedEventArgs("Table1"))
RaiseEvent PropertyChanged(Me, New ComponentModel.PropertyChangedEventArgs("ChangesDetected"))
End Sub
Private Sub Row_Changed(ByVal sender As Object, ByVal e As System.Data.DataRowChangeEventArgs)
Select Case e.Action
Case System.Data.DataRowAction.Change
If e.Row.RowState <> System.Data.DataRowState.Unchanged Then
_changesDetected += 1
End If
End Select
End Sub
Private Function CreateTableWithData() As System.Data.DataTable
Dim newTable As New System.Data.DataTable
Dim columnID As New System.Data.DataColumn("ID", GetType(Guid))
Dim columnA As New System.Data.DataColumn("ColumnA", GetType(String))
Dim columnB As New System.Data.DataColumn("ColumnB", GetType(String))
newTable.Columns.AddRange({columnID, columnA, columnB})
newTable.PrimaryKey = {newTable.Columns(0)}
For i = 0 To 5
Dim dr = newTable.NewRow
dr("ID") = Guid.NewGuid
dr("ColumnA") = String.Format("Column A Row {0}", i.ToString)
dr("ColumnB") = String.Format("Column B Row {0}", i.ToString)
newTable.Rows.Add(dr)
Next
Return newTable
End Function
Public Event PropertyChanged(sender As Object, e As System.ComponentModel.PropertyChangedEventArgs) Implements System.ComponentModel.INotifyPropertyChanged.PropertyChanged
End Class
So, in the MergeTables method, I make a change to the first row in _table2 and I make a change in the second row of _table1.
Because I made a change the first row in _table1, the _table1.GetChanges method returns a DataTable with all of the changed rows (just the first row in this case).
I then merge the table containing the changes with _table2 and indicate that I want to preserve changes.
Once that merge is completed I know that the results are preserving the changes that I had made before the merge and that the table will contain the new data as well (so long as there were no conflicts). The result of merging the incoming data into the changes table will resolution of any conflicts in data.
After I have that resolved table I can safely merge into the original _table1 table indicating that preserve change = false. Because passing false as the preserve changes parameter results in no RowState changes for the original data everything works perfectly fine! My changes are preserved And the RowStates are not modified!
Happy Coding!
-Frinny

Make DataRepeater bound to List(Of Object) update?

What is the correct way to bind a List(Of Object) to a DataRepeater? Can you provide example code for this?
I have been racking my brains on this and while I can get an already filled list to show in the repeater, subsequent changes to the list have no effect on the DataRepeater.
Ultimately I hope to use this to bind to a dictionary, if that is possible, but I cannot even get the basics working here.
The data repeater is added on the form design surface, with 3 labels and a progress bar in the ItemTemplate. The code I have attempted (where DutData is the DataRepeater) to setup the list and repeater is then:
Public Class BurnIn
Public Shared currentDuts As New Dictionary(Of UInteger, DeviceUnderTest) ' Collection of all current DUTs.
Dim bs As New BindingSource
Dim testTemp As Boolean = False
Dim testList As New List(Of DeviceUnderTest)
Private Sub BurnIn_Load() Handles Me.Load
'...
' Add two items to the dictionary and populate them
currentDuts.Add(0, New DeviceUnderTest(Me.user, 0))
currentDuts.Item(0).RackBay = "012345678901"
currentDuts.Item(0).AssemblySerial = "123456789"
currentDuts.Item(0).SetProgram(1, "Program1")
currentDuts.Add(currentDuts.Count, New DeviceUnderTest(Me.user, 1))
currentDuts.Item(1).RackBay = "109876543210"
currentDuts.Item(1).AssemblySerial = "1319A5126"
currentDuts.Item(1).SetProgram(1, "Program1")
' Copy the items to the test list.
testList.Add(currentDuts.Item(0))
testList.Add(currentDuts.Item(1))
testTemp = True
' Setup the binding source, data source and data bindings.
bs.DataSource = testList
LocationLabel.DataBindings.Add("Text", bs, "RackBay")
DutLabel.DataBindings.Add("Text", bs, "AssemblySerial")
ProgramLabel.DataBindings.Add("Text", bs, "Program")
DutProgress.DataBindings.Add("Value", bs, "Progress")
DutData.DataSource = testList
'...
Me.Show()
End Sub
Then to test adding or removing list items:
Private Sub Button1_Click() Handles Button1.Click
If testTemp = False Then
' Add an item to the dictionary and populate it.
currentDuts.Add(currentDuts.Count, New DeviceUnderTest(Me.user, 1))
currentDuts.Item(1).RackBay = "109876543210"
currentDuts.Item(1).AssemblySerial = "1319A5126"
currentDuts.Item(1).SetProgram(1, "Program1")
' Copy the item to the test list.
testList.Add(currentDuts.Item(1))
testTemp = True
Else
' Remove the item from the dictionary and the list.
currentDuts.Remove(1)
testList.Remove(testList.Item(1))
testTemp = False
End If
End Sub
End Class
First thing is to replace your List with a BindingList
Dim testList As New BindingList(Of DeviceUnderTest)

Referencing an Unbound DataGridView Without Specifically Naming It?

I am using 3 unbound DataGridView controls to display certain information. To load the information into those DGVs, I am pulling the information from an encrypted file, decrypting it, parsing the information, then trying to fill the DGVs with that information. The loading from the file is called by the menu item click. Here is what I have so far:
Private Sub miCLoad_Click(ByVal sender As System.Object, ByVal e As System.EventArgs)
Handles miCLoad.Click
Dim FilePath As String = "C:\FList\CList.clt"
Dim LoadFile As New SaveandLoad.SaveAndLoad
Dim FileRead As New Simple3Des("MyPassword")
Dim FileString As String = FileRead.ReadFile(FilePath)
With LoadFile
.WhichList = dgCourses
.FilePath = FilePath
.DecryptedString = FileRead.DecryptData(FileString)
.dgList = dgCourses
End With
Call LoadFile.LoadFile()
End Sub
Public Class SaveandLoad
Public Property WhichList As New DataGridView
Public Property FilePath As String
Public Property DecryptedString As String
Public Property EncryptedString As String
Public Property dgList As Control
Public Sub LoadFile()
Dim dgRow As DataGridViewRow
Dim dgCell As DataGridViewTextBoxCell
Dim Lines() As String = DecryptedString.Split(vbLf)
Dim LinesList As List(Of String) = Lines.ToList
LinesList.RemoveAt(Lines.Length - 1)
For Each Line As String In LinesList
Dim Fields() As String = Line.Split(",")
dgRow = New DataGridViewRow
For x = 0 To (WhichList.Columns.Count - 1) Step 1
dgCell = New DataGridViewTextBoxCell
dgCell.Value = Fields(x).ToString
dgRow.Cells.Add(dgCell)
Next
WhichList.Rows.Add(dgRow)
Next
Select Case WhichList.Name
Case "dgCourses"
frmFacultyList.dgCourses = WhichList
frmFacultyList.dgCourses.Refresh()
WhichList.Dispose()
Case "dgFList"
frmFacultyList.dgFList = WhichList
frmFacultyList.dgFList.Refresh()
WhichList.Dispose()
Case "dgSList"
frmFacultyList.dgSList = WhichList
frmFacultyList.dgSList.Refresh()
WhichList.Dispose()
End Select
MsgBox("List Successfully Loaded", vbOKOnly, "Load")
End Sub
I want to be able to reference (or fill) a DGV without using 'select case' or 'if-then' statements. This will be too inefficient once I start adding the many other DGVs, that will be added in the future. Therefore, the title is the main question. I am using VS Express 2010.
I don't know VB too much, however, I'll post my solution in C# (may be helpfull in some way....)
DataGridView myDGV;
foreach (var item in this.Controls)
{
if (item.GetType() == typeof(DataGridView))
{
if (((DataGridView)item).Name == WhichList.Name)
{
//Cannot assing to 'item' here, because it is a 'foreach iteration variable'
//However you can save the variable for later use.
myDGV = (DataGridView)item;
}
}
}
myDGV = WhichList;
// different approach
DataGridView myDGV = (DataGridView)this.Controls.Find(WhichList.Name, false).First();
myDGV = WhichList;
Here is what worked for me in VB.NET:
Dim FormControls As New frmFacultyList.ControlCollection(frmFacultyList)
For Each DGV As DataGridView In FormControls
If WhichList.Name = DGV.Name Then
DGV = WhichList
DGV.Refresh()
End If
Next
Make an instance of the control collection then search specifically for DGVs using For Each. Simple and efficient.