Adding Multiple TreeNode Levels with Multi threading - vb.net

i'm having alot of trouble trying to get a background worker properly functioning to populate a couple of TreeView Nodes. In said TreeView i have multiple levels of Nodes. For example
FileName
Model
Test & Result.
Model refers to a MicroStation Model (CAD Drawing Program), easiest way to explain it is a Sheet within a Spreadsheet.
I'm using a FileDialog to select files, once the files are selected each filename is added to the TreeView with its own Node.
The idea is that the program will then open each file, scan each model and add a sub TreeNode under the files Node wih the type of test and the result.
The DoWork function for the Background worker is below. I have removed alot of the code to simply my post. But there are 7 "tests" that the program does, i have included 2.
In the below example, "CheckFonts" is a function that just counts the text elements in a file and returns a number.
Private Sub BackgroundWorker1_DoWork(sender As Object, e As System.ComponentModel.DoWorkEventArgs) Handles BackgroundWorker1.DoWork
For i As Integer = 0 To m_CountTo
If BackgroundWorker1.CancellationPending Then
e.Cancel = True ' Set Cancel to True
Exit For
End If
Dim sError As String = ""
Dim bBorderFound As Boolean = False
Dim oDesignFile As DesignFile
Dim oModel As ModelReference
Dim oFontNode As New TreeNode
Dim oFontResultNode As New TreeNode
For Each oNode As TreeNode In trvItems.Nodes
Dim ustn As New MicroStationDGN.Application
oDesignFile = ustn.OpenDesignFileForProgram(oNode.Text, True)
For Each oModel In oDesignFile.Models
'####### Checks for Items on Default Level #######
If bDefaultPass = True Then
Dim iDefaultItems As Long
iDefaultItems = DefaultItems(oModel)
If iDefaultItems > 0 Then
sDefaultMessage = "There are " & iDefaultItems & " items on the Default Level"
bDefaultPass = False
Else
sDefaultMessage = "There are no items on the Default Level"
bDefaultPass = True
End If
End If
'####### Checks for Non Standard Fonts #######
If bFontPass = True Then
Dim iFontCheck As Long
iFontCheck = CheckFonts(oModel)
If iFontCheck > 0 Then
sFontMessage = "There are " & iFontCheck & " Text Elements that use a Non Standard Font."
bFontPass = False
ElseIf iFontCheck = -99999 Then
sFontMessage = "There are some corrupt or invalid Fonts used in the Design File"
bFontPass = False
Else
sFontMessage = "All Text Elements use the Correct Font"
bFontPass = True
End If
End If
Next ' End Model
oFontNode = oNode.Nodes.Add("Font Check")
oFontResultNode = oFontNode.Nodes.Add("")
If bFontPass = True Then
oFontResultNode.Text = "PASS - " & sFontMessage
oFontResultNode.ImageIndex = 0
oNode.Collapse()
Else
oFontResultNode.Text = "FAIL - " & sFontMessage
bPass = False
oFontResultNode.ImageIndex = 1
oFontNode.ImageIndex = 1
oNode.Expand()
oFontNode.Expand()
End If
oDefaultItemsNode = oNode.Nodes.Add("Default Items Check")
oDefaultItemsResultNode = oDefaultItemsNode.Nodes.Add("")
If bDefaultPass = True Then
oDefaultItemsResultNode.Text = "PASS - " & sDefaultMessage
oDefaultItemsResultNode.ImageIndex = 0
oNode.Collapse()
Else
oDefaultItemsResultNode.Text = "FAIL - " & sDefaultMessage
oDefaultItemsResultNode.ImageIndex = 1
oDefaultItemsResultNode.ImageIndex = 1
oNode.Expand()
bPass = False
End If
Next ' End File
Next
End Sub
I have worked with Background Workers before but this is a bit more complex to what i have done, i understand that you cant update controls from a different thread and that Invoke is used to pass it information. But i'm confused how to do it with multiple nodes. The best example I saw was here
Adding nodes to treeview with Begin Invoke / Invoke
But with multiple nodes the code became quite confusing and didn't work. The error message keeps coming up regarding the Invoke / BeginInvoke being called.
So, i guess my main question is where i would call the Invoke command to best make use of the Background worker?
Thanks in advance!!

Related

Get focus on unbound textbox when form returns no records

I'm a little stumped.
I've got an MS Access front end application for an SQL Server back end. I have an orders form with a list box that, when selected and a "Notes" button is clicked will open another form of notes. This is a continuous form and has a data source (linked table - a view) from the back end database.
When the notes button is clicked in the main orders form, it passes a filter and an OpenArgs string to the Notes form in this code:
Private Sub cmdItemNotes_Click()
Dim i As Integer
Dim ordLine As Boolean
Dim line As Integer
Dim args As String
If Me.lstOrders.ItemsSelected.count = 1 Then
ordLine = False
With Me.lstOrders
For i = 0 To .ListCount - 1
If .selected(i) Then
If .Column(16, i) = "Orders" Then
ordLine = True
line = .Column(0, i)
End If
End If
Next i
End With
If ordLine Then
args = "txtLineID|" & line & "|txtCurrentUser|" & DLookup("[User]", "tblUsers", "[Current] = -1") & "|txtSortNum|" & _
Nz(DMax("[SortNum]", "dbo_vwInvoiceItemNotesAll", "[LineID] = " & line), 0) + 1 & "|"
DoCmd.OpenForm "frmInvoiceItemNotes", , , "LineID = " & line, , , args
Else
'Potting order notes
End If
Else: MsgBox "Please select one item for notes."
End If
Here is my On Load code for the Notes form:
Private Sub Form_Load()
Dim numPipes As Integer
Dim ArgStr As String
Dim ctl As control
Dim ctlNam As String
Dim val As String
Dim i As Integer
ArgStr = Me.OpenArgs
numPipes = Len(ArgStr) - Len(Replace(ArgStr, "|", ""))
For i = 1 To (numPipes / 2)
ctlNam = Left(ArgStr, InStr(ArgStr, "|") - 1)
Set ctl = Me.Controls(ctlNam)
ArgStr = Right(ArgStr, Len(ArgStr) - (Len(ctlNam) + 1))
val = Left(ArgStr, InStr(ArgStr, "|") - 1)
ctl.Value = val
ArgStr = Right(ArgStr, Len(ArgStr) - (Len(val) + 1))
Next i
End Sub
This code executes fine. The form gets filtered to only see the records (notes) for the line selected back in the orders form.
Because this is editing a table in the back end, I use stored procedures in a pass through query to update the table, not bound controls. The bound controls in the continuous form are for displaying current records only. So... I have an unbound textbox (txtNewNote) in the footer of the form to type a new note, edit an existing note, or post a reply to an existing note.
As stated above, the form filters on load. Everything works great when records show. But when it filters to no records, the txtNewNote textbox behaves quite differently. For instance, I have a combo box to mention other users. Here is the code after update for the combo box:
Private Sub cmbMention_AfterUpdate()
Dim ment As String
If Me.txtNewNote = Mid(Me.txtNewNote.DefaultValue, 2, Len(Me.txtNewNote.DefaultValue) - 2) Then
Me.txtNewNote.Value = ""
End If
If Not IsNull(Me.cmbMention) Then
ment = " #" & Me.cmbMention & " "
If Not InStr(Me.txtNewNote, ment) > 0 Then
Me.txtNewNote = Me.txtNewNote & ment
End If
End If
With Me.txtNewNote
.SetFocus
.SelStart = Len(Nz(Me.txtNewNote, ""))
End With
End Sub
The problem occurs with the line
.SelStart = Len(Nz(Me.txtNewNote, ""))
When there are records to display, it works. When there are no records to display, it throws the Run-time error 2185 "You can't reference a property or method for a control unless the control has the focus."
Ironically, if I omit this line and make the .SetFocus the last line of code in the sub, the control is in focus with the entire text highlighted.
Why would an unbound textbox behave this way just because the filter does not show records?
Thanks!

Background worker Picturebox gif

I have a picturebox with a gif (the gif works). My program is written to open up a document file that is located on a drive on the other side of the country, so it takes some time.
I have set picturebox.visible = true when the program starts the connection process, but the gif does not animate during this process; but only when idling.
A Google search led me to the background worker control, but in all the examples I found, I could not find one that could either:
Just have the gif running in a background worker permanently (I assume this wouldn't use up too much memory, as it is just a gif)
or
Have the gif animate while the application is connecting
Can anyone help?
From the comments, I'm assuming that this entire thing needs to be in a backgroundworker.dowork, but I just keep getting errors because I'm changing controls here.
Private Sub btnLoad_Click(sender As Object, e As EventArgs) Handles btnLoad.Click
'Displaying "Loading"
picLoading.Visible = True
tblMain.Visible = False
'If the application has loaded at least one sheet
If FirstLoad = 2 Then
worksheet.Cells(1, 31).Value = ""
workbook.Save()
workbook.Close(False)
End If
Filter = cmbFilter.Text
'Go to page 1
CurrentPage = 0
workbook = APP.Workbooks.Open(Path & "\" & Filter & ".xlsx")
worksheet = workbook.Worksheets("Sheet1")
If Convert.ToString(worksheet.Cells(1, 31).Value) <> "" Then
MsgBox("The sheet for " & Filter & " is currently being used by " & Convert.ToString(worksheet.Cells(1, 31).Value) & ".")
workbook.Close(False)
Else
FirstLoad = 2 'Indicate that the application has loaded at least one sheet
worksheet.Cells(1, 31).Value = User
'Make all textboxes and comboboxes visible = false
txtSearch.Visible = False : btnSort.Visible = False : btnSortUrgency.Visible = False : btnSave.Visible = False : btnPrevious.Visible = False : btnNext.Visible = False
Label1.Visible = False
Label2.Visible = False
'Count entries and calculate how many pages there are
Entries = worksheet.Range("B1048576").End(Excel.XlDirection.xlUp).Row 'Count how many entries there are (the first row always counts as an entry)
TotalPages = Math.Ceiling(Entries / 8) - 1 'Calculate how many pages there are
If TotalPages = (Entries / 8) - 1 Then
TotalPages = TotalPages + 1
End If
'Determine how many entries are on the last page
LastEntries = Entries
While LastEntries > 8 'Keep subtracting by 8 to determine how many entries are going to be on the last page
LastEntries = LastEntries - 8
End While
'Add categories for the Urgency comboboxes
Me.cmbUrgency1.DrawMode = DrawMode.OwnerDrawFixed
Me. cmbUrgency1.DropDownStyle = ComboBoxStyle.DropDownList
Me. cmbUrgency1.ItemHeight = 15
Me. cmbUrgency1.BeginUpdate()
cmbUrgency1.Items.Clear()
Me. cmbUrgency1.Items.Add("1")
Me. cmbUrgency1.Items.Add("2")
Me. cmbUrgency1.Items.Add("3")
Me. cmbUrgency1.Items.Add("4")
cmbUrgency1.EndUpdate()
Me. cmbUrgency2.DrawMode = DrawMode.OwnerDrawFixed
Me. cmbUrgency2.DropDownStyle = ComboBoxStyle.DropDownList
Me. cmbUrgency2.ItemHeight = 15
Me. cmbUrgency2.BeginUpdate()
cmbUrgency2.Items.Clear()
Me. cmbUrgency2.Items.Add("1")
Me. cmbUrgency2.Items.Add("2")
Me. cmbUrgency2.Items.Add("3")
Me. cmbUrgency2.Items.Add("4")
cmbUrgency2.EndUpdate()
Me. cmbUrgency3.DrawMode = DrawMode.OwnerDrawFixed
Me. cmbUrgency3.DropDownStyle = ComboBoxStyle.DropDownList
Me. cmbUrgency3.ItemHeight = 15
Me. cmbUrgency3.BeginUpdate()
cmbUrgency3.Items.Clear()
Me. cmbUrgency3.Items.Add("1")
Me. cmbUrgency3.Items.Add("2")
Me. cmbUrgency3.Items.Add("3")
Me. cmbUrgency3.Items.Add("4")
Urgency3.EndUpdate()
Me.Urgency4.DrawMode = DrawMode.OwnerDrawFixed
Me.Urgency4.DropDownStyle = ComboBoxStyle.DropDownList
Me.Urgency4.ItemHeight = 15
Me.Urgency4.BeginUpdate()
Urgency4.Items.Clear()
Me.Urgency4.Items.Add("1")
Me.Urgency4.Items.Add("2")
Me.Urgency4.Items.Add("3")
Me.Urgency4.Items.Add("4")
Urgency4.EndUpdate()
Dim DataCollection As New AutoCompleteStringCollection()
txtBlank.AutoCompleteMode = AutoCompleteMode.Suggest
txtBlank.AutoCompleteSource = AutoCompleteSource.CustomSource
AddItems(DataCollection)
txtBlank.AutoCompleteCustomSource = DataCollection
txtBlank2.AutoCompleteMode = AutoCompleteMode.Suggest
txtBlank2.AutoCompleteSource = AutoCompleteSource.CustomSource
AddItems(DataCollection)
txtBlank2.AutoCompleteCustomSource = DataCollection
txtBlank3.AutoCompleteMode = AutoCompleteMode.Suggest
txtBlank3.AutoCompleteSource = AutoCompleteSource.CustomSource
AddItems(DataCollection)
txtBlank3.AutoCompleteCustomSource = DataCollection
txtBlank3.AutoCompleteMode = AutoCompleteMode.Suggest
txtBlank3.AutoCompleteSource = AutoCompleteSource.CustomSource
AddItems(DataCollection)
txtBlank3.AutoCompleteCustomSource = DataCollection
workbook.Save()
Call DisplayInfo()
End If
End Sub

Why does Microsoft Visual Basic skip over part of this code

I'm trying to make some labels on my Form to be visible, but i don't want to use a lot of if statements, but for some reason whenever i put Me.Controls(lbl).Visbel = True in a for or do loop it skips the whole loop. The code worked perfectly the way I wanted it until i got an error for calling Dim lbl = Controls("Label" & counter_3) for the whole class instead of in the From_load private sub. Sometimes i can get it to work, but only one label is visible
Dim chararray() As Char = word_list(random_word).ToCharArray
Dim lbl = "Label" & counter_3
For Each item In chararray
If item = Nothing Then
Else
word_list(counter_2) = item.ToString()
counter_2 += 1
End If
Next
For Each item In chararray
If item = Nothing Then
Else
counter_3 += 1
Me.Controls(lbl).Visible = True
MsgBox(item & " " & counter_3)
End If
Next
I've also tried. In both the loops are completely skipped over. I know this because the MsgBox's don't appear.
Dim chararray() As Char = word_list(random_word).ToCharArray
Dim lbl = Controls("Label" & counter_3)
For Each item In chararray
If item = Nothing Then
Else
word_list(counter_2) = item.ToString()
counter_2 += 1
End If
Next
For Each item In chararray
If item = Nothing Then
Else
counter_3 += 1
lbl.Visble = True
MsgBox(item & " " & counter_3)
End If
Next
The thing that I am noticing is that you are creating a Char array based on a random word returned from your word_list, you then iterate through the Char array using the count of the character in the array as an index into your word_list, if the amount of characters in your word exceeds the amount of words in your list you will get an error and since this error is in the Forms Load event it will be swallowed and all the code after it will be aborted. There are also other issues that I would change like making sure all declarations have a type and I would probably use the Controls.Find Method instead and check that it has an actual object. But what I would probably do first is move your code to a separate Subroutine and call it after your IntializeComponent call in the Forms Constructor(New) Method.
Something like this.
Public Sub New()
' This call is required by the designer.
InitializeComponent()
' Add any initialization after the InitializeComponent() call.
YourMethod
End Sub
Public Sub YourMethod()
Dim chararray() As Char = word_list(random_word).ToCharArray
Dim lbl As Control() = Controls.Find("Label" & counter_3, True)
For Each item In chararray
If item = Nothing Then
Else
word_list(counter_2) = item.ToString()
counter_2 += 1
End If
Next
For Each item In chararray
If item = Nothing Then
Else
counter_3 += 1
If lbl.Length > 0 Then
lbl(0).Visible = True
Else
MsgBox("Control not Found")
End If
MsgBox(item & " " & counter_3)
End If
Next
End Sub

Background Worker and SaveDialog

I am very new with Background worker control. I have an existing project that builds file but throughout my project while building files I get the deadlock error.
I am trying to solve it by creating another project that will only consist out of the background worker. I will then merge them.
My problem is I don't know where it will be more effective for my background worker to be implemented and also the main problem is how can I use the SaveDialog with my background worker? I need to send a parameter to my background worker project telling it when my file is being build en when it is done.
This is where my file is being build:
srOutputFile = New System.IO.StreamWriter(strFile, False) 'Create File
For iSeqNo = 0 To iPrintSeqNo
' Loop through al the record types
For Each oRecord As stFileRecord In pFileFormat
If dsFile.Tables.Contains(oRecord.strRecordName) Then
' Loop through al the records
For Each row As DataRow In dsFile.Tables(oRecord.strRecordName).Rows
' Check record id
If oRecord.strRecordId.Length = 0 Then
bMatched = True
Else
bMatched = (CInt(oRecord.strRecordId) = CInt(row.Item(1)))
End If
' Match records
If iSeqNo = CInt(row.Item(0)) And bMatched Then
strRecord = ""
' Loop through al the fields
For iLoop = 0 To UBound(oRecord.stField)
' Format field
If oRecord.stField(iLoop).iFieldLength = -1 Then
If strRecord.Length = 0 Then
strTmp = row.Item(iLoop + 1).ToString
Else
strTmp = strDelimiter & row.Item(iLoop + 1).ToString
End If
ElseIf oRecord.stField(iLoop).eFieldType = enumFieldType.TYPE_VALUE Or _
oRecord.stField(iLoop).eFieldType = enumFieldType.TYPE_AMOUNT_CENT Then
strTmp = row.Item(iLoop + 1).ToString.Replace(".", "").PadLeft(oRecord.stField(iLoop).iFieldLength, "0")
strTmp = strTmp.Substring(strTmp.Length - oRecord.stField(iLoop).iFieldLength)
Else
strTmp = row.Item(iLoop + 1).ToString.PadRight(oRecord.stField(iLoop).iFieldLength, " ").Substring(0, oRecord.stField(iLoop).iFieldLength)
End If
If oRecord.stField(iLoop).iFieldLength > -1 And (bForceDelimiter) And strRecord.Length > 0 Then
strTmp = strDelimiter & strTmp
End If
strRecord = strRecord & strTmp
Next
' Final delimiter
If (bForceDelimiter) Then
strRecord = strRecord & strDelimiter
End If
srOutputFile.WriteLine(strRecord)
End If
Next
End If
Next
Next
You could try this:
Private locker1 As ManualResetEvent = New System.Threading.ManualResetEvent(False)
Private locker2 As ManualResetEvent = New System.Threading.ManualResetEvent(False)
Dim bOpenFileOK As Boolean
Dim myOpenFile As OpenFileDialog = New OpenFileDialog()
Private Sub FileOpener()
While Not bTerminado
If myOpenFile.ShowDialog() = System.Windows.Forms.DialogResult.OK Then
bOpenFileOK = True
Else
bOpenFileOK = False
End If
locker2.Set()
locker1.WaitOne()
End While
End Sub
' Detonator of the action
Private Sub Button1_Click(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles Button1.Click
Dim tFileOp As Thread = New Thread(AddressOf FileOpener)
tFileOp.SetApartmentState(ApartmentState.STA)
tFileOp.Start()
' Start BackgroundWorker
BW1.RunWorkerAsync()
End Sub
Private Sub AsyncFunctionForBW(ByVal args As ArrayList)
'[...]
'Change options dinamically for the OpenFileDialog
myOpenFile.Filter = ""
myOpenFile.MultiSelect = True
'Calling the FileDialog
locker1.Set()
locker2.WaitOne()
locker1.Reset()
locker2.Reset()
If bOpenFileOK Then
myStream = myOpenFile.OpenFile()
'[...]
End If
End Sub
It's a little bit complicated but it works.
ManualResetEvents interrupt the execution of code (if they are told to stop) when reached until you use .Set(). If you use .WaitOne() you set it in stop mode, so it will stop again when reached.
This code defines two ManualResetEvents. When you click the Button1 starts the function FileOpener() in a new Thread, and then starts the BackgroundWorker. The FileOpener() function shows a FileOpenDialog and waits in the locker1 so when you use locker1.Set() the function shows the file dialog.
As the myOpenFile is a "global" variable (as well as bOpenFileOK), once the user select the file (or not) you could detect the dialog result (bOpenFileOK) and the selected file.

Using single panel containing checkboxes for multiple treeview nodes (Images & Code Attached)

I am a VB.NET beginner.
I want to achieve following:
Node1 is clicked which opens a panel containing check-boxes.
The user will click a few check-boxes.
The user clicks node2 which will export check-box information to an Excel sheet column, and reset the panel.
New information entered on panel is exported to an adjacent column in the same Excel sheet used in step3.
The above process continues for 90 nodes.
How do I do the first part in steps 3, 4 and 5?
This is my first try which is not working:
Dim oXL As Excel.Application
Dim oWB As Excel.Workbook
Dim oSheet As Excel.Worksheet
oWB = oXL.Workbooks.Open("F:\open.xlsx")
oSheet = oWB.Worksheets("Sheet1")
'I am not able to think clearly on following loop
For i = 1 To 3
For j = 1 To 90
If CheckBox1.Checked Then
oSheet.Cells(i, j).value = "1"
Else : oSheet.Cells(i, j).value = "0"
End If
If CheckBox2.Checked Then
oSheet.Cells(i, j).value = "1"
Else : oSheet.Cells(i, j).value = "0"
End If
If CheckBox3.Checked Then
oSheet.Cells(i, j).value = "1"
Else : oSheet.Cells(i, j).value = "0"
End If
Next
Next
'Following works
CheckBox1.Checked() = False
CheckBox2.Checked() = False
CheckBox3.Checked() = False
ComboBox1.ResetText()
What you clearly need is a way to store the user's selections in memory, before saving them to the spreadsheet. There are several ways you could do this, but given your inexperience I suggest you consider the simplest, which is to define a basic class to represent the user's selections for a single node, and an array – where each item is an instance of the class – to store the entire set of user selections.
Define the node selection class – to represent selections for a single node:
Public Class NodeSelection
Public CheckA As Boolean
Public PickAIndex As Integer = -1
Public CheckB As Boolean
Public PickBIndex As Integer = -1
Public CheckC As Boolean
Public PickCIndex As Integer = -1
Public ItemProcessed As Boolean
End Class
Define your variables – in your form class (not in a sub):
Private _userPicks(89) As NodeSelection 'Array of user's selections
Private _previousIndex As Integer = -1 'Used to record the previously selected node
Instantiate the array items – in the form's Load event:
'Instantiate the class for each element of the array
For i As Integer = 0 To _userPicks.Count - 1
_userPicks(i) = New NodeSelection
Next
Keeping track of user selections:
Whenever a new node is selected, you need to update the array item for the previously selected node then reset the controls for the current node. This is best done in the treeview's AfterSelect event:
Private Sub TreeView1_AfterSelect(sender As Object, e As System.Windows.Forms.TreeViewEventArgs) Handles TreeView1.AfterSelect
'Exit if the click is on the parent node (Node0)
If e.Node.GetNodeCount(False) > 0 Then Return
UpdateNodeInfo()
'If the currently selected node has already been processed,
'restore user selection values to the controls,
'otherwise reset the controls
With _userPicks(e.Node.Index)
CheckBox1.Checked = If(.ItemProcessed, .CheckA, False)
ComboBox1.SelectedIndex = If(.ItemProcessed, .PickAIndex, -1)
CheckBox2.Checked = If(.ItemProcessed, .checkB, False)
ComboBox2.SelectedIndex = If(.ItemProcessed, .PickBIndex, -1)
CheckBox3.Checked = If(.ItemProcessed, .checkC, False)
ComboBox3.SelectedIndex = If(.ItemProcessed, .PickCIndex, -1)
End With
'Color the previous selection so the user can see it's been processed
If _previousIndex >= 0 Then TreeView1.Nodes(0).Nodes(_previousIndex).BackColor = Color.AntiqueWhite
'Record this (selected) node's index for updating when the next node is selected
_previousIndex = e.Node.Index
End Sub
Private Sub UpdateNodeInfo()
If _previousIndex < 0 Then Return 'No item has been set yet
With _userPicks(_previousIndex)
.CheckA = CheckBox1.Checked
.PickAIndex = If(.CheckA, ComboBox1.SelectedIndex, -1)
.CheckB = CheckBox2.Checked
.PickBIndex = If(.CheckB, ComboBox2.SelectedIndex, -1)
.checkC = CheckBox3.Checked
.PickCIndex = If(.checkC, ComboBox3.SelectedIndex, -1)
.ItemProcessed = True 'Record the fact the item has already been processed
End With
End Sub
Writing values to the spreadsheet:
Notice that I put the array item update routine in a separate procedure. This is because you will have to update the final selection before writing it all out to the spreadsheet. I presume you will have a button to save their selections, so you just need to call the UpdateNodeInfo sub from there before iterating over the array and writing the values. Here is how you might iterate over the values and update the spreadsheet:
For i As Integer = 0 To _userPicks.Count - 1
With _userPicks(i)
oSheet.Cells(3, i + 2) = "Node" & (i + 1).ToString
oSheet.Cells(9, i + 2) = "Node" & (i + 1).ToString
oSheet.Cells(4, i + 2) = If(.ItemProcessed AndAlso .CheckA, 1, 0)
oSheet.Cells(5, i + 2) = If(.ItemProcessed AndAlso .CheckB, 1, 0)
oSheet.Cells(6, i + 2) = If(.ItemProcessed AndAlso .checkC, 1, 0)
oSheet.Cells(10, i + 2) = If(.ItemProcessed AndAlso .CheckA, ComboBox1.Items(.PickAIndex).ToString, "")
oSheet.Cells(11, i + 2) = If(.ItemProcessed AndAlso .CheckB, ComboBox1.Items(.PickBIndex).ToString, "")
oSheet.Cells(12, i + 2) = If(.ItemProcessed AndAlso .checkC, ComboBox1.Items(.PickCIndex).ToString, "")
End With
Next
I assume you already know how to open, save and close the spreadsheet, so I'll leave that side of it up to you. Get familiar with the methods outlined here and post another question if you don't.
Finally
The above is a fairly simple way to achieve what you're trying to do. If you think you may need to add more functionality to your class, you should look at substituting public members for properties – some would say you should do that anyway – and you may want to consider storing the complete set of user selections in a List(Of T) object rather than an array.