ProgressBar Woes. Getting UI thread to update while reading database - vb.net

I'm having trouble getting a progress bar to update. I'd be okay just with a moving marquee bar. Basically, I'm reading a database routine in a SqliteReader.vb class. I'm new to visual basic, and I'm sure I need to use the worker_DoWork routine, but I'm not sure how to expose my variables coming from Form1: graphData, graphComputations, m_debug to the worker_DoWork sub. How is this usually done?
Public Class SqliteReader
Public Sub ReadDataBase
End Sub
End Class
This is updating a graph (zedgraph element) on the main form, Form1.vb. I call the progressbar from the main form like this:
ProgressBar.Initialize(channelArray, computationArray, m_debug)
ProgressBar.vb below:
Partial Public Class ProgressBar
Dim DataAcquisition As New SqliteReader
Dim WithEvents worker As New BackgroundWorker
Public Sub Initialize(ByRef graphData As Channels(), ByRef graphComputations As Computations(), ByVal m_debug As Integer)
DataAcquisition = SqliteReader.GetInstance()
Me.Show()
Me.Update()
Dim Update_Thread As Thread(AddressOf Update_ThreadExecute)
Update_Thread.Priority = ThreadPriority.Normal
Update_Thread.Start()
DataAcquisition.ParseEntireDatabase(graphData, graphComputations, m_debug)
Me.Close()
End Sub
Private Sub ProgressBarStart(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
DataAcquisition = SqliteReader.GetInstance()
progress.Style = ProgressBarStyle.Marquee
worker.WorkerReportsProgress = True
worker.WorkerSupportsCancellation = True
worker.RunWorkerAsync()
End Sub
Private Sub worker_DoWork(ByVal sender As Object, ByVal e As System.ComponentModel.DoWorkEventArgs) Handles worker.DoWork
Dim worker As BackgroundWorker = DirectCast(sender, BackgroundWorker)
DataAcquisition = SqliteReader.GetInstance()
' I probably need
' DataAcquisition.ParseEntireDatabase(graphData, graphComputations, m_debug)
' here... but how do I expose graphdata, graphcomputations and m_debug to this sub?
End Sub
Private Sub worker_ProgressChanged(ByVal sender As Object, ByVal e As System.ComponentModel.ProgressChangedEventArgs) Handles worker.ProgressChanged
dataProgress = CInt(((DataAcquisition.currentRow + 1) / DataAcquisition.totalRows) * 100)
progress.Value = dataProgress
End Sub

RunWorkerAsync has a second version that takes a parameter. You can use that to pass any values (or references) your worker needs.
That said, you shouldn't be updating form elements from inside the worker. Your worker should fire the ProgressChanged event when you want the UI to update, and you handle it there. That one also has a version that can send a value back. (Or many values if you send back an array, list, or custom class.)
The last step in this is that you need to actually fire ProgressChanged. DataAcquisition.ParseEntireDatabase may not do that, in which case using it won't allow this method to work.

If graphData, graphComputations, m_debug are already members of ProgressBar and worker_DoWork is a member of ProgressBar, then you have nothing more to do. You should be able to access them directly.

For Rapunzo, above.. My Final Solution was this:
Partial Public Class ProgressBar
Dim _mDataAcquisition As New SqliteReader
Public Property DataProgress As Integer = 0
Dim WithEvents _mProgressWorker As New BackgroundWorker
Public Sub Initialize(ByRef graphData As List(Of Channels), ByRef auxData As List(Of Channels), _
ByRef graphComputations As List(Of Computations))
_mDataAcquisition = SqliteReader.GetInstance()
Show()
Update()
_mDataAcquisition.ParseEntireDatabase(graphData, auxData, graphComputations)
Close()
End Sub
Private Sub ProgressBarStart(ByVal sender As System.Object, ByVal e As EventArgs) Handles MyBase.Load
progress.Style = ProgressBarStyle.Blocks
_mProgressWorker.WorkerReportsProgress = True
_mProgressWorker.WorkerSupportsCancellation = True
_mProgressWorker.RunWorkerAsync()
progress.Visible = True
progress.Maximum = 100
progress.Value = 0
End Sub
Public Sub WorkerProgressChanged()
progress.Value = DataProgress
Invalidate()
End Sub
Private Sub WorkerRunWorkerCompleted(ByVal sender As Object, _
ByVal e As System.ComponentModel.RunWorkerCompletedEventArgs) _
Handles _mProgressWorker.RunWorkerCompleted
progress.Visible = False
progress.Value = 0
Close()
End Sub
From there, just call ProgressBar.Initialize to start it
To update:
ProgressBar.DataProgress = CInt((currentIt / totalIt) * 100)
ProgressBar.WorkerProgressChanged()
and to end:
ProgressBar.DataProgress = 100
ProgressBar.WorkerProgressChanged()
Hope this helps.

Related

Show and Hide a label in a panel in VB

I was surprised to find that in VB one can not easily set the font and backcolour of a tooltip item by it's default properties so I thought I would create my own.
Private aTimer As New System.Timers.Timer
Private Sub Panel1_MouseHover(sender As Object, e As EventArgs) Handles Panel1.MouseHover
Hover("How's about that")
End Sub
Private Sub Hover(ByRef Tip As String)
If Not lblToolTip.Visible Then
lblToolTip.Text = Tip
lblToolTip.Location = Me.PointToClient(MousePosition)
lblToolTip.Visible = True
lblToolTip.BringToFront()
StartTimer()
End If
End Sub
Private Sub StartTimer()
aTimer.Interval = 2000
AddHandler aTimer.Elapsed, New System.Timers.ElapsedEventHandler(AddressOf aTimer_Elapsed)
aTimer.Start()
End Sub
Sub aTimer_Elapsed(ByVal sender As Object, ByVal e As System.Timers.ElapsedEventArgs)
aTimer.Stop()
lblToolTip.Visible = False
End Sub
Everything goes ok until I try to hide the label when I get the error -
'Cross - thread operation Not valid: Control 'lblToolTip' accessed from a thread other than the thread it was created on.'
I have researched the error for a couple of days but I am afraid it is a bit too deep a thread for an 80 old year after a stroke. I would appreciate any pointers to direct me to the solution.
You could have used a Forms Timer and not had this problem.
Since you didn't, replace your aTimer_Elapsed method with this.
Sub aTimer_Elapsed(ByVal sender As Object, ByVal e As System.Timers.ElapsedEventArgs)
aTimer.Stop()
Me.BeginInvoke(Sub()
lblToolTip.Visible = False
End Sub)
End Sub
Try use Delegate
Private aTimer As New System.Timers.Timer
Private Delegate Sub SafeCallDelegate(ByVal label As Label, ByVal Visible As Boolean, ByVal Text As String)
Private Sub Panel1_MouseHover(sender As Object, e As EventArgs) Handles Panel1.MouseHover
Hover("How's about that")
End Sub
Private Sub StartTimer()
aTimer.Interval = 2000
AddHandler aTimer.Elapsed, New System.Timers.ElapsedEventHandler(AddressOf aTimer_Elapsed)
aTimer.Start()
End Sub
Sub aTimer_Elapsed(ByVal sender As Object, ByVal e As System.Timers.ElapsedEventArgs)
aTimer.Stop()
SetLabelProperty(lblToolTip, False)
End Sub
Private Sub SetLabelProperty(ByVal label As Label, ByVal Visible As Boolean, Optional ByVal Text As String = "")
If label.InvokeRequired Then
label.Invoke(New SafeCallDelegate(AddressOf SetLabelProperty), New Object() {label, Visible, Text})
Else
label.Visible = Visible
label.Location = Me.PointToClient(MousePosition)
label.Text = Text
lblToolTip.BringToFront()
End If
End Sub
Private Sub Hover(ByRef Tip As String)
If Not lblToolTip.Visible Then
SetLabelProperty(lblToolTip, True, Tip)
StartTimer()
End If
End Sub
Look this example:
Update Text Box Properly when Cross-threading in Visual Basic (VS 2012 V11)
This was the result
Public Sub ToolTip_Draw(sender As Object, e As DrawToolTipEventArgs)
e.DrawBackground()
e.DrawBorder()
e.Graphics.DrawString(e.ToolTipText, New Font("Arial", 10), System.Drawing.Brushes.Black, New PointF(2, 2))
End Sub
Public Sub ToolTip_Popup(sender As Object, e As PopupEventArgs)
Dim Sz As Size = TextRenderer.MeasureText(sender.GetToolTip(e.AssociatedControl), New Font("Arial", 10))
e.ToolTipSize = New Size(Sz.Width, Sz.Height + 4)
End Sub
The backcolour is set in the properties. The size is determined by the PipUp.

How to track multiple BackgroundworkerX.Runworkercompleted operations

I am trying to use a single handler to cover the end of multiple backgroundworker activities and cannot find a way to get the information about the specific backgroundworker using the backgroundworkercompleted event.
My code to catch the event is as below:
Private Sub BGx_RunWorkerCompleted(ByVal sender As Object, ByVal e As System.ComponentModel.RunWorkerCompletedEventArgs) Handles BackgroundWorker1.RunWorkerCompleted, BackgroundWorker2.RunWorkerCompleted, BackgroundWorker3.RunWorkerCompleted, BackgroundWorker4.RunWorkerCompleted, BackgroundWorker5.RunWorkerCompleted, BackgroundWorker6.RunWorkerCompleted, BackgroundWorker7.RunWorkerCompleted, BackgroundWorker8.RunWorkerCompleted
'Do work here based on completed Backgroundworker
For BG = 1 To 8
If Not DSWorkers(BG).IsBusy Then
If DStatus(BG) = -2 Then : DStatus(BG) = -1 : End If
End If
Next
Complete()
End Sub
There is nothing on the "Do Work Here" section because I do not know how to capture and have been unable to find details of the backgroundworkercompleted event id.
Please - any pointers as to how I can identify the specific completed BackgroundWorker
As with all event handlers, the sender parameter is a reference to the object that raised the event, so you can access the actual BackgroundWorker that has completed its work via that. If you need some data other than that, you assign it to the e.Result property in the DoWork event handler and get it back from the e.Result property in the RunWorkerCompleted event handler. e.Result works for getting data out of the DoWork event handler much as e.Argument works for getting data in.
Check this out for some examples of using BackgroundWorker objects, including passing data out using e.Result. You might also like to checkout my own BackgroundMultiWorker class, which basically combines the functionality of multiple BackgroundWorker objects into a single BackgroundMultiWorker object. It identifies each task using a token.
EDIT:
Here's an example that may help with this issue and your task in general:
Imports System.ComponentModel
Imports System.Threading
Public Class Form1
Private ReadOnly resultsByWorker As New Dictionary(Of BackgroundWorker, BackgroundWorkerResult)
Private ReadOnly rng As New Random
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
'The NumericUpDown is used to select the index of a BackgroundWorker to cancel.
With NumericUpDown1
.DecimalPlaces = 0
.Minimum = 0
.Maximum = 9
End With
End Sub
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
'Create 10 BackgroundWorkers and run them.
For i = 1 To 10
Dim worker As New BackgroundWorker
resultsByWorker.Add(worker, New BackgroundWorkerResult)
AddHandler worker.DoWork, AddressOf workers_DoWork
AddHandler worker.RunWorkerCompleted, AddressOf workers_RunWorkerCompleted
worker.WorkerSupportsCancellation = True
worker.RunWorkerAsync()
Next
End Sub
Private Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click
Dim index = Convert.ToInt32(NumericUpDown1.Value)
Dim worker = resultsByWorker.Keys.ToArray()(index)
If worker.IsBusy Then
'Cancel the BackgroundWorker at the specified index.
worker.CancelAsync()
End If
End Sub
Private Sub workers_DoWork(sender As Object, e As DoWorkEventArgs)
Dim worker = DirectCast(sender, BackgroundWorker)
'Do work for a random number of seconds between 10 and 20.
Dim period = rng.Next(10, 20 + 1)
For i = 0 To period
If worker.CancellationPending Then
e.Cancel = True
Return
End If
'Simulate work.
Thread.Sleep(1000)
Next
'The work was completed without being cancelled.
e.Result = period
End Sub
Private Sub workers_RunWorkerCompleted(sender As Object, e As RunWorkerCompletedEventArgs)
Dim worker = DirectCast(sender, BackgroundWorker)
Dim result = resultsByWorker(worker)
If e.Cancelled Then
result.WasCancelled = True
Else
result.Result = CInt(e.Result)
End If
Dim workers = resultsByWorker.Keys.ToArray()
If Not workers.Any(Function(bgw) bgw.IsBusy) Then
'All work has completed so display the results.
Dim results As New List(Of String)
For i = 0 To workers.GetUpperBound(0)
worker = workers(i)
result = resultsByWorker(worker)
results.Add($"Worker {i} {If(result.WasCancelled, "was cancelled", $"completed {result.Result} iterations")}.")
Next
MessageBox.Show(String.Join(Environment.NewLine, results))
End If
End Sub
End Class
Public Class BackgroundWorkerResult
Public Property WasCancelled As Boolean
Public Property Result As Integer
End Class
Here is that example reworked to use a single instance of the BackgroundMultiWorker is linked to instead of multiple instances of the BackgroundWorker class.
Imports System.Threading
Public Class Form1
Private WithEvents worker As New BackgroundMultiWorker With {.WorkerSupportsCancellation = True}
Private ReadOnly results(9) As BackgroundWorkerResult
Private ReadOnly rng As New Random
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
'The NumericUpDown is used to select the index of a BackgroundWorker to cancel.
With NumericUpDown1
.DecimalPlaces = 0
.Minimum = 0
.Maximum = results.GetUpperBound(0)
End With
End Sub
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
'Create 10 BackgroundWorkers and run them.
For i = 0 To results.GetUpperBound(0)
results(i) = New BackgroundWorkerResult
worker.RunWorkerAsync(i)
Next
End Sub
Private Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click
Dim index = Convert.ToInt32(NumericUpDown1.Value)
If worker.IsBusy(index) Then
'Cancel the BackgroundWorker at the specified index.
worker.CancelAsync(index)
End If
End Sub
Private Sub worker_DoWork(sender As Object, e As DoWorkEventArgs) Handles worker.DoWork
'Do work for a random number of seconds between 10 and 20.
Dim period = rng.Next(10, 20 + 1)
For i = 0 To period
If worker.IsCancellationPending(e.Token) Then
e.Cancel = True
Return
End If
'Simulate work.
Thread.Sleep(1000)
Next
'The work was completed without being cancelled.
e.Result = period
End Sub
Private Sub workers_RunWorkerCompleted(sender As Object, e As RunWorkerCompletedEventArgs) Handles worker.RunWorkerCompleted
Dim result = results(CInt(e.Token))
If e.Cancelled Then
result.WasCancelled = True
Else
result.Result = CInt(e.Result)
End If
If Not worker.IsBusy() Then
'All work has completed so display the results.
Dim output As New List(Of String)
For i = 0 To results.GetUpperBound(0)
result = results(i)
output.Add($"Task {i} {If(result.WasCancelled, "was cancelled", $"completed {result.Result} iterations")}.")
Next
MessageBox.Show(String.Join(Environment.NewLine, output))
End If
End Sub
End Class
Public Class BackgroundWorkerResult
Public Property WasCancelled As Boolean
Public Property Result As Integer
End Class

Stuck with an odd stack overflow

I'm writing this program in Visual Basic .NET to organize different fields of data, and I'm using profile slots through application settings to store the data for users. But, I ran into a stack overflow error in my SlotSelect.vb class. My best plausible guess for why this happens is that I'm using the wrong kind of variable container in the below sauce code, but my dilemma is that I don't know what specifically is going wrong.
The code that the vshost is saying is the cause for the overflow was written from some code that I looked up on MSDN and other places for referring to objects in other classes, and I tried using other variants of it to see if it was any different. So far, nothing has worked, and it doesn't stop the error while compiling, much less in the code markup--it only catches it when it starts the application in debug after it finishes building.
Here's the sauce code for SlotSelect.vb. Since most of the other unrelated classes (and this one) lead into MainForm.vb, I'll include its sauce too. The location vshost gave as the error is on the line where variable _MainForm is defined for Flaglister.MainForm to be used within SlotSelect.vb.
SlotSelect.vb
Public Class SlotSelect
' Class variables
Private _MainForm As Flaglister.MainForm = New Flaglister.MainForm
Private _SaveSlot As Flaglister.SaveSlot = New Flaglister.SaveSlot
Private _Misc As Flaglister.Misc = New Flaglister.Misc
Private _FlagsTextBox As System.Windows.Forms.TextBox = Flaglister.MainForm.FlagsTextBox
Private _VarsTextBox As System.Windows.Forms.TextBox = Flaglister.MainForm.VarsTextBox
Private _HackNameTextBox As System.Windows.Forms.TextBox = Flaglister.MainForm.HackNameTextBox
Private _RomCodeTextBox As System.Windows.Forms.TextBox = Flaglister.MainForm.RomCodeTextBox
Private _NotesTextBox As System.Windows.Forms.TextBox = Flaglister.MainForm.NotesTextBox
Private _ExpandedCheckBox As System.Windows.Forms.CheckBox = Flaglister.MainForm.ExpandedCheckBox
' Slot selection main execution subs
Friend Sub _0()
Try
' Disable FlagsTextBox
_FlagsTextBox.DeselectAll()
_FlagsTextBox.ClearUndo()
_FlagsTextBox.Clear()
_FlagsTextBox.Enabled = False
' Disable VarsTextBox
_VarsTextBox.DeselectAll()
_VarsTextBox.ClearUndo()
_VarsTextBox.Clear()
_VarsTextBox.Enabled = False
' Disable HackNameTextBox
_HackNameTextBox.DeselectAll()
_HackNameTextBox.ClearUndo()
_HackNameTextBox.Clear()
_HackNameTextBox.Enabled = False
' Disable RomCodeTextBox
_RomCodeTextBox.DeselectAll()
_RomCodeTextBox.ClearUndo()
_RomCodeTextBox.Clear()
_RomCodeTextBox.Enabled = False
' Disable NotesTextBox
_NotesTextBox.DeselectAll()
_NotesTextBox.ClearUndo()
_NotesTextBox.Clear()
_NotesTextBox.Enabled = False
Catch
Call _Misc.ErrorClose()
End Try
End Sub
Friend Sub _1()
Try
' Load flaglist
_FlagsTextBox.DeselectAll()
_FlagsTextBox.ClearUndo()
_FlagsTextBox.Clear()
_FlagsTextBox.Enabled = True
_FlagsTextBox.Text = My.Settings.Flaglist_1
' Load varlist
_VarsTextBox.DeselectAll()
_VarsTextBox.ClearUndo()
_VarsTextBox.Clear()
_VarsTextBox.Enabled = True
_VarsTextBox.Text = My.Settings.Varlist_1
' Load project name
_HackNameTextBox.DeselectAll()
_HackNameTextBox.ClearUndo()
_HackNameTextBox.Clear()
_HackNameTextBox.Enabled = True
_HackNameTextBox.Text = My.Settings.Hackname_1
' Load ROM codename
_RomCodeTextBox.DeselectAll()
_RomCodeTextBox.ClearUndo()
_RomCodeTextBox.Clear()
_RomCodeTextBox.Enabled = True
_RomCodeTextBox.Text = My.Settings.Romcode_1
' Load other notes
_NotesTextBox.DeselectAll()
_NotesTextBox.ClearUndo()
_NotesTextBox.Clear()
_NotesTextBox.Enabled = True
_NotesTextBox.Text = My.Settings.Notes_1
Catch
Call _Misc.ErrorClose()
End Try
End Sub
Friend Sub _2()
Try
' Load flaglist
_FlagsTextBox.DeselectAll()
_FlagsTextBox.ClearUndo()
_FlagsTextBox.Clear()
_FlagsTextBox.Enabled = True
_FlagsTextBox.Text = My.Settings.Flaglist_2
' Load varlist
_VarsTextBox.DeselectAll()
_VarsTextBox.ClearUndo()
_VarsTextBox.Clear()
_VarsTextBox.Enabled = True
_VarsTextBox.Text = My.Settings.Varlist_2
' Load project name
_HackNameTextBox.DeselectAll()
_HackNameTextBox.ClearUndo()
_HackNameTextBox.Clear()
_HackNameTextBox.Enabled = True
_HackNameTextBox.Text = My.Settings.Hackname_2
' Load ROM codename
_RomCodeTextBox.DeselectAll()
_RomCodeTextBox.ClearUndo()
_RomCodeTextBox.Clear()
_RomCodeTextBox.Enabled = True
_RomCodeTextBox.Text = My.Settings.Romcode_2
' Load other notes
_NotesTextBox.DeselectAll()
_NotesTextBox.ClearUndo()
_NotesTextBox.Clear()
_NotesTextBox.Enabled = True
_NotesTextBox.Text = My.Settings.Notes_2
Catch
Call _Misc.ErrorClose()
End Try
End Sub
Friend Sub _3()
Try
' Load flaglist
_FlagsTextBox.DeselectAll()
_FlagsTextBox.ClearUndo()
_FlagsTextBox.Clear()
_FlagsTextBox.Enabled = True
_FlagsTextBox.Text = My.Settings.Flaglist_3
' Load varlist
_VarsTextBox.DeselectAll()
_VarsTextBox.ClearUndo()
_VarsTextBox.Clear()
_VarsTextBox.Enabled = True
_VarsTextBox.Text = My.Settings.Varlist_3
' Load project name
_HackNameTextBox.DeselectAll()
_HackNameTextBox.ClearUndo()
_HackNameTextBox.Clear()
_HackNameTextBox.Enabled = True
_HackNameTextBox.Text = My.Settings.Hackname_3
' Load ROM codename
_RomCodeTextBox.DeselectAll()
_RomCodeTextBox.ClearUndo()
_RomCodeTextBox.Clear()
_RomCodeTextBox.Enabled = True
_RomCodeTextBox.Text = My.Settings.Romcode_3
' Load other notes
_NotesTextBox.DeselectAll()
_NotesTextBox.ClearUndo()
_NotesTextBox.Clear()
_NotesTextBox.Enabled = True
_NotesTextBox.Text = My.Settings.Notes_3
Catch
Call _Misc.ErrorClose()
End Try
End Sub
' Save all slots
Friend Sub SlotSaveAll()
Call _SaveSlot.FlaglistSave()
Call _SaveSlot.VarlistSave()
Call _SaveSlot.HacknameSave()
Call _SaveSlot.RomcodeSave()
Call _SaveSlot.NotesSave()
Call _SaveSlot.ExpandedSave()
End Sub
End Class
MainForm.vb
Public Class MainForm
' Class-level variables
Private _SlotSelect As New Flaglister.SlotSelect
Private _SaveSlot As New Flaglister.SaveSlot
Private _Misc As New Flaglister.Misc
' Startup/Shutdown events
Sub MainForm_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
If Slot0RadioButton.Checked = True Then
Call _SlotSelect._0()
End If
End Sub
Sub MainForm_Deactivate(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Deactivate
My.Settings.Save()
Me.Close()
End Sub
' Form object events
Private Sub Slot0RadioButton_CheckedChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Slot0RadioButton.CheckedChanged
If Slot0RadioButton.Checked = True Then
Call _SlotSelect._0()
End If
End Sub
Private Sub Slot1RadioButton_CheckedChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Slot1RadioButton.CheckedChanged
If Slot1RadioButton.Checked = True Then
Call _SlotSelect._1()
End If
End Sub
Private Sub Slot2RadioButton_CheckedChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Slot2RadioButton.CheckedChanged
If Slot2RadioButton.Checked = True Then
Call _SlotSelect._2()
End If
End Sub
Private Sub Slot3RadioButton_CheckedChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Slot3RadioButton.CheckedChanged
If Slot3RadioButton.Checked = True Then
Call _SlotSelect._3()
End If
End Sub
Private Sub FlagsTextBox_TextChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles FlagsTextBox.TextChanged
Call _SaveSlot.FlaglistSave()
End Sub
Private Sub VarsTextBox_TextChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles VarsTextBox.TextChanged
Call _SaveSlot.VarlistSave()
End Sub
Private Sub HackNameTextBox_TextChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles HackNameTextBox.TextChanged
Call _SaveSlot.HacknameSave()
End Sub
Private Sub RomCodeTextBox_TextChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles RomCodeTextBox.TextChanged
Call _SaveSlot.RomcodeSave()
End Sub
Private Sub NotesTextBox_TextChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles NotesTextBox.TextChanged
Call _SaveSlot.NotesSave()
End Sub
Private Sub ExpandedCheckBox_CheckedChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles ExpandedCheckBox.CheckedChanged
Call _SaveSlot.ExpandedSave()
End Sub
End Class
What's the problem in the code?
Here's some additional details about me and my program.
Computer
System: Seven x64 w/ AMD Sempron
Compiler: Microsoft Visual Basic 2010 Express
Application
Framework: Microsoft .NET version 3.5
Root Namespace: Flaglister
Assembly name: Flaglister
Application Type: Windows Forms Application
Sometimes I think too much, so I'm sorry if the answer is obvious to many and I don't see it.
EDIT
Here's a screenshot of the callstack at its current position when the error is thrown, and another screenshot of the popup error box. I would've never guessed to add these.
The issue here is recursive initialization. MainForm contains:
Private _SlotSelect As New Flaglister.SlotSelect
And SlotSelect contains:
Private _MainForm As Flaglister.MainForm = New Flaglister.MainForm
They keep calling each other's constructor, causing your stack overflow. You can fix this by passing in _MainForm as an argument in SlotSelect's constructor. Like so:
Public Class SlotSelect
' Class variables
Private _MainForm As Flaglister.MainForm
' (truncated for space)
Public Sub New(ByVal mainForm As Flaglister.MainForm)
Me._MainForm = mainForm
End Sub
'...
And in MainForm.vb, pass SlotSelect's constructor the current instance:
Private _SlotSelect As New Flaglister.SlotSelect(Me)
I'm guessing it is because in your class you are declaring and instantiating a new instance of MainForm
Private _MainForm As Flaglister.MainForm = New Flaglister.MainForm
Then in the MainForm class you are declaring and instantiating a new instance of the SlotSelect class
Private _SlotSelect As New Flaglister.SlotSelect
So the two classes just "bounce" back and forth creating new instances of each other, eventually giving you the stack overflow.
You are in an infinite loop.
In slotselect you have
Private _MainForm As Flaglister.MainForm = New Flaglister.MainForm
which creates a new main form, and then in MainForm you have
Private _SlotSelect As New Flaglister.SlotSelect
which creates a new slotselect.
Make one object the parent and one the child, and hand the parent reference in the constructor e.g.
Class SlotSelect
Private _MainForm As MainForm
Sub New(mf as MainForm)
_MainForm=mf
End Sub
...
End Class

Iteration Not Working As Intended

I'm using a DO interation to loop a function I'm using to test for internet connectivity. The code is working fine, except that when one of the tests is satisfied the loop stops. I want this to continue in the background while the program is running. How can I get this to work?
Private Sub checkInternet()
Dim InetChecker As Boolean
InetChecker = CheckForInternetConnection()
Do While LabelCount.Text <> ""
Thread.Sleep(10)
If InetChecker = True Then
Dim image = My.Resources.greenbar
PictureBox4.Image = image
Else
Thread.Sleep(10)
Dim image = My.Resources.redbar
PictureBox4.Image = image
'NoInetConnError.Show()
End If
Loop
End Sub
Your assistance would be greatly appreciated, thanks.
Put a BackgroundWorker on your form (you will find it in the Components section of the Toolbox).
In the Properties window set WorkerReportsProgress to True for your BackgroundWorker.
Insert the following code to your form
Private connected As Boolean
Private Sub BackgroundWorker1_DoWork(ByVal sender As Object, ByVal e As DoWorkEventArgs) _
Handles BackgroundWorker1.DoWork
While True
Dim online = CheckForInternetConnection()
If online <> connected Then
connected = online
BackgroundWorker1.ReportProgress(CInt(online))
End If
Thread.Sleep(500)
End While
End Sub
Private Sub BackgroundWorker1_ProgressChanged(ByVal sender As Object, ByVal e As ProgressChangedEventArgs) _
Handles BackgroundWorker1.ProgressChanged
Dim online As Boolean = CBool(e.ProgressPercentage)
If online Then
PictureBox4.Image = My.Resources.greenbar
Else
PictureBox4.Image = My.Resources.redbar
End If
End Sub
Private Sub Form_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) _
Handles MyBase.Load
' Start the background worker
BackgroundWorker1.RunWorkerAsync()
End Sub
Note that Sub BackgroundWorker1_DoWork runs on a separate thread and does not freeze your form while it is running.
It would be best to do something like this in a Timer and not in a loop.
Private Sub Timer1_Tick(ByVal sender As Object, ByVal e As EventArgs) Handles Timer1.Tick
If CheckForInternetConnection Then
PictureBox4.Image = My.Resources.greenbar
Else
PictureBox4.Image = My.Resources.redbar
End If
End Sub
If you have access to .Net framework 3+ then you could use the DispatcherTimer class which essentially creates an interval (set at whatever length you require) which you can handle the tick event for. When the tick event is raised, you can do your internet connection check.
Modifying the MSDN example for your situation, you could do something like this:
' DispatcherTimer setup
dispatcherTimer = New Threading.DispatcherTimer()
AddHandler dispatcherTimer.Tick, AddressOf dispatcherTimer_Tick
dispatcherTimer.Interval = New TimeSpan(0,0,1) ' Or however long you want
dispatcherTimer.Start()
Private Sub dispatcherTimer_Tick(ByVal sender As Object, ByVal e As EventArgs)
' Checks to see whether an internet connection is still available etc
checkInternet()
' Forcing the CommandManager to raise the RequerySuggested event
CommandManager.InvalidateRequerySuggested()
End Sub

How To Report Progress Changed vb.net?

I need to report progress changed. Consider the following code:
Public Class Calculator
Public Event CalculationProgress (ByVal sender As Object, ByVal e As MyCalculationProgressEventArgs)
Public Function Calculate(..)..
' Perform calculation here ...
' Reporting proggress
Dim args As New MyCalculationProgressEventArgs(myobj, myValue)
RaiseEvent CalculationProgress (Me, args)
...
End Class
*** Another class
Private WithEvents calculator As Calculator
Private Function PerformCalculation(ByVal obj As Object) As CalcParams
Dim params As CalcParams = CType(obj, CalcParams)
calculator = GetCalculator()
....
Return params.result = calculator.Calculate
End Function
Private Sub calculationWorker_DoWork(ByVal sender As Object, ByVal e As DoWorkEventArgs) _
Handles calculationWorker.DoWork
Dim calcResult As MyType = PerformCalculation(CType(e.Argument, MyType ))
e.Result = calcResult
End Sub
Private Sub calculationWorker_ProgressChanged(ByVal sender As Object, ByVal e As ProgressChangedEventArgs) _
Handles calculationWorker.ProgressChanged
CType(Parent.Parent, MainWindow).pbcCurrentProgress.Value = e.ProgressPercentage
End Sub
How and where should I subscribe to CalculationProgress event to call
calculationWorker.ReportProgress(MyCalculationProgressEventArgs.Percent)
?
Are you using a BackgroundWorker object here? If so what you want to do is to subscribe to the CalculationProgress event inside of the calculationWorker_DoWork event handler. You didn't post any information on MyType, so I'll assume you'll need to alter my code to get the Calculator instance.
Private Sub calculationWorker_DoWork(ByVal sender As Object, ByVal e As DoWorkEventArgs) _
Handles calculationWorker.DoWork
Dim calcResult As MyType = PerformCalculation(CType(e.Argument, MyType ))
Dim calc = calcResult.Calculator
AddHandler calc.CalculationProgress, AddressOf HandleCalculationProgress
...
RemoveHandler calc.CalculationProgress, AddressOf HandleCalculationProgress
e.Result = calcResult
End Sub
You would do this after your GetCalculator call, and before calling Calculate.