Why is my invoke adding 10 seconds onto grid loading time? - vb.net

I've attempted to add a method invoker to stop my error log being spammed with "Bounds cannot be changed while locked."
This has solved my issue, however...It has added an extra 10 seconds onto the loading time of my RadGridView.
I looked at https://www.telerik.com/forums/bounds-cannot-be-changed-while-locked to setup my invoker but there isn't much else that I can see to help with my issue.
I've attached a sample of my code below, any help would be appreciated.
Private Sub bgw_initialLoad_DoWork(sender As Object, e As DoWorkEventArgs)
Try
liveDS = New DataSet
Dim dsholder As DataSet = GetDataFromSQL("LoadData")
Dim dt1 As DataTable = dsholder.Tables(0)
Dim dt_1 As DataTable = dt1.Copy()
dt_1.TableName = "Customer"
liveDS.Tables.Add(dt_1)
Dim dt2 As DataTable = dsholder.Tables(1)
Dim dt_2 As DataTable = dt2.Copy()
dt_2.TableName = "Orders"
liveDS.Tables.Add(dt_2)
Dim dt3 As DataTable = dsholder.Tables(2)
Dim dt_3 As DataTable = dt3.Copy()
dt_3.TableName = "OrderLine"
liveDS.Tables.Add(dt_3)
If RadGridView.InvokeRequired Then
RadGridView.Invoke(New MethodInvoker(AddressOf SetupDataSources))
Else
SetupDataSources()
End If
Catch ex As Exception
sendCaughtError(ex)
End Try
End Sub
Private Sub SetupDataSources()
If liveDS.Tables.Count > 1 Then
RadGridView.DataSource = liveDS.Tables("Customer")
liveOrdersTemplate.DataSource = liveDS.Tables("Orders")
liveOrdersTemplate2.DataSource = liveDS.Tables("OrderLine")
End If
End Sub
Private Sub bgw_initialLoad_RunWorkerCompleted(sender As Object, e As RunWorkerCompletedEventArgs)
Try
RadGridView.DataSource = liveDS.Tables("Customer")
Dim template As New GridViewTemplate()
template.DataSource = liveDS.Tables("Orders")
RadGridView.MasterTemplate.Templates.Add(template)
Dim template2 As New GridViewTemplate()
template2.DataSource = liveDS.Tables("OrderLine")
RadGridView.Templates(0).Templates.Add(template2)
Dim relation As New GridViewRelation(RadGridView.MasterTemplate)
relation.ChildTemplate = template
relation.ParentColumnNames.Add("Invoice Customer")
relation.ChildColumnNames.Add("InvoiceCode")
RadGridView.Relations.Add(relation)
Dim relation2 As New GridViewRelation(RadGridView.Templates(0))
relation2.ChildTemplate = template2
relation2.ParentColumnNames.Add("OrderNo")
relation2.ChildColumnNames.Add("OrderNo")
RadGridView.Relations.Add(relation2)
FormatGrid()
SplitContainer2.Panel1.Enabled = True
SplitContainer1.Panel2.Enabled = True
refreshMainGrid()
HideLoadingGif()
Catch ex As Exception
sendCaughtError(ex)
End Try
End Sub

Debugging threads can be hard, trust me. This isn't a "real" answer, but a bunch of tips which may help - which is what I hope will happen.
There are dedicated windows in the debug menu which may help. I started with this webpage when I was wondering what was happening to my application and why it wasn't obvious why it was happening.
Also, while your parallel thread is running, it may "silent crash" if your IDE isn't set to pause on every crash, in which case it won't return a value but will just stay silent. Make sure at least these options are set:
And don't forget to show this window while debugging: (previous image showed Threads and Call stack instead, while they are good to have around while debugging it's the parallel stacks which I was going for)
One last thing: such a big delay may be database related. I'm not saying that it is, but you should be aware of the possibility.
Now the following isn't part of the answer per se, but is more of a friendly advice: put your invoke logic in SetupDataSources() instead, this way wherever it's called you'll be thread safe. Like this:
Private Sub SetupDataSources()
If RadGridView.InvokeRequired Then
RadGridView.Invoke(Sub() SetupDataSources())
End If
If liveDS.Tables.Count > 1 Then
RadGridView.DataSource = liveDS.Tables("Customer")
liveOrdersTemplate.DataSource = liveDS.Tables("Orders")
liveOrdersTemplate2.DataSource = liveDS.Tables("OrderLine")
End If
End Sub
Best of luck... you might need some ;)

Related

datagridview last programmatically changed cell does not get included in changedrows

VB.net app changes values in a datagridview programmatically. The values are all as they should be, but the save routine (dtShipments is the datatable that is the source for the datagridview)
Dim dtChanges As DataTable = dtShipments.getchanges()
If more than one has changed, dtChanges is always missing the last row.
In the routine that changes the cell values, I have tried DatagridView1.EndEdit and DatagridView1.CommitEdit, but the behavior is the same. I even tried adding a SendKeys.Send(vbTab) line, since hitting the tab key when making the changes manually is enough to get all the changes to show up in .GetChanges.
What am I missing?
code per request:
Private Sub btnAssign_Click(sender As Object, e As EventArgs) Handles btnAssign.Click
strModErr = "Form1_btnAssign_Click"
Dim sTruck As String = ""
Try
sTruck = Me.DataGridView2.SelectedCells(0).Value.ToString
For Each row As DataGridViewRow In DataGridView1.SelectedRows
row.Cells("Truck").Value = sTruck
Next
DataGridView1.CommitEdit(DataGridViewDataErrorContexts.Commit)
Catch ex As Exception
WriteErrorToLog(Err.Number, strModErr + " - " + Err.Description)
End Try
End Sub
Private Function SaveChanges() As Boolean
strModErr = "Form1_SaveChanges"
Dim Conn As New SqlConnection(My.Settings.SQLConnectionString)
Dim sSQL As String = "UPDATE fs_Shipments SET Truck = #Truck, Stop = #Stop WHERE SalesOrder = #SO"
Dim cmd As New SqlCommand(sSQL, Conn)
Dim sSO, sTruck As String
Dim iStop As Integer = 0
Try
DataGridView1.EndEdit()
DataGridView1.ClearSelection()
Dim dtChanges As DataTable = dtShipments.getchanges() 'DataRowState.Modified
If Not dtChanges Is Nothing Then
Conn.Open()
For Each row As DataRow In dtChanges.Rows
sSO = row("SalesOrder").ToString
sTruck = row("Truck").ToString
iStop = CInt(row("Stop").ToString)
With cmd.Parameters
.Clear()
.AddWithValue("#SO", sSO)
.AddWithValue("#Truck", sTruck)
.AddWithValue("#Stop", iStop)
End With
cmd.ExecuteNonQuery()
Next
End If
Return True
Catch ex As Exception
WriteErrorToLog(Err.Number, strModErr + " - " + Err.Description)
Return False
End Try
End Function
I am not exactly 100% sure why this happens. The problem appears to be specific to when the user “selects” the cells in the first grid, and then calls the SaveChanges code “BEFORE” the user has made another selection in the first grid. In other words, if the user “selects” the rows to “assign” the truck to in grid 1, then, “AFTER” the “selected” cells have been filled with the selected truck, THEN, the user selects some other cell in grid 1, THEN calls the save changes code. In that case the code works as expected.
All my attempts at committing the changes, either failed or introduced other issues. I am confident a lot of this has to do with the grids SelectedRows collection. IMHO, this looks like a risky way to set the cell values, I would think a more user-friendly approach may be to add a check box on each row and assign the truck values to the rows that are checked. But this is an opinion.
Anyway, after multiple attempts, the solution I came up with only further demonstrates why using a BindingSource is useful in many ways. In this case, it appears the DataTable is not getting updated with the last cell change. Again, I am not sure “why” this is, however since it appears to work using a BindingSource, I can only assume it has something to do with the DataTable itself. In other words, before the “Save” code is executed, we could call the tables AcceptChanges method, but then we would lose those changes. So that is not an option.
To help, below is a full (no-fluff) example. In the future, I highly recommend you pull out the parts of the code that do NOT pertain to the question. Example, all the code that saves the changes to the DB is superfluous in relation to the question… so remove it. The more unnecessary code you add to your question only increases the number of SO users that will “ignore” the question. If you post minimal, complete and working code that reproduces the problem so that users can simply copy and paste without having to add addition code or remove unnecessary code will increase you chances of getting a good answer. Just a heads up for the future.
The solution below simply adds a BindingSource. The BindingSource’s DataSource is the DataTable dtShipments then the BindingSource is used a DataSource to DataGridView1. Then in the SaveChanges method, “before” the code calls the dtShipment.GetChanges() method, we will call the BindingSources.ResetBinding() method which should complete the edits to the underlying data source, in this case the DataTable dtShipments.
If you create a new VS-VB winforms solution, drop two (2) grids and two (2) buttons onto the form as shown below, then change the button names to “btnAssign” and “btnApplyChanges.” The assign button will add the selected truck in grid two to the selected rows in grid one. The apply changes button simply resets the binding source and displays a message box with the number of rows that were changed in the dtShipments DataTable. It should be noted, that the code calls the dtShipments.AcceptChanges() method to clear the changes. Otherwise, the code will update changes that have already been made.
Dim dtShipments As DataTable
Dim dtTrucks As DataTable
Dim shipBS As BindingSource
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
dtShipments = GetShipmentsDT()
shipBS = New BindingSource()
shipBS.DataSource = dtShipments
dtTrucks = GetTrucksDT()
dtShipments.AcceptChanges()
DataGridView1.DataSource = shipBS
DataGridView2.DataSource = dtTrucks
End Sub
Private Function GetShipmentsDT() As DataTable
Dim dt = New DataTable()
dt.Columns.Add("ID", GetType(String))
dt.Columns.Add("Truck", GetType(String))
dt.Rows.Add(1)
dt.Rows.Add(2)
dt.Rows.Add(3)
dt.Rows.Add(4)
Return dt
End Function
Private Function GetTrucksDT() As DataTable
Dim dt = New DataTable()
dt.Columns.Add("Truck", GetType(String))
For index = 1 To 10
dt.Rows.Add("Truck" + index.ToString())
Next
Return dt
End Function
Private Sub btnAssign_Click(sender As Object, e As EventArgs) Handles btnAssign.Click
Dim sTruck = DataGridView2.SelectedCells(0).Value.ToString
'Dim drv As DataRowView
For Each row As DataGridViewRow In DataGridView1.SelectedRows
'drv = row.DataBoundItem
'drv("Truck") = sTruck
row.Cells("Truck").Value = sTruck
Next
'DataGridView1.CommitEdit(DataGridViewDataErrorContexts.Commit)
'shipBS.ResetBindings(True)
'DataGridView1.CurrentCell = DataGridView1.Rows(0).Cells(0)
End Sub
Private Function SaveChanges() As Boolean
Try
shipBS.ResetBindings(True)
Dim dtChanges As DataTable = dtShipments.GetChanges()
If (dtChanges IsNot Nothing) Then
MessageBox.Show("There are " & dtChanges.Rows.Count & " rows changed in the data table")
' update sql DB
dtShipments.AcceptChanges()
End If
Return True
Catch ex As Exception
Return False
End Try
End Function
Private Sub btnApplyChanges_Click(sender As Object, e As EventArgs) Handles btnApplyChanges.Click
Dim ChangesMade As Boolean
ChangesMade = SaveChanges()
End Sub
Lastly, since you are using VB… I highly recommend that you set the “Strict” option to “ON.” When this option is ON, it will flag possible errors that you may be missing. To do this for all future VB solutions, open VS, close any solutions that may be open, then go to Tools->Options->Projects and Solutions->VB Defaults, then set “Option Strict” to “On.” The default setting is off.
I hope this makes sense and helps.

Secondary thread causes "Application has stopped working" crashes even when invoking

I have an application which has a form with a DataGridView bound to a BindingSource, which is bound to a DataTable:
bsList.DataSource = dsData
bsList.DataMember = "List"
dgvList.DataSource = bsList
The underlying data which populates dsData.Tables("List") can change whilst the user is working so to combat this I have a background thread which routinely checks the database for changes and updates dsData.Tables("List"). It also changes the colour of any row where another user is currently working.
However, users report that when this background updating functionality is enabled the application routinely CTDs with no application error message. I have been unable to reproduce this and my attempt to log the crashes via writing to a log file in Private Sub MyApplication_UnhandledException(sender As Object, e As UnhandledExceptionEventArgs) Handles Me.UnhandledException hasn't worked as the log file is never written to, suggesting this event is never triggered.
The thread is instantiated like this:
LiveUpdating = New Thread(AddressOf UpdateUserLocation) With {.IsBackground = True}
LiveUpdating.Start()
This is the UpdateUserLocation sub:
Public Sub UpdateUserLocation()
Do While My.Settings.customBackgroundUpdating = True And formLoaded = True
UserLocations.Clear()
dtUsers = CLS_USERS.GetUsersSequence(winUser)
dtProgress = DAC.GetProgress()
For Each CandRow As DataRow In dsHHData.Tables("List").Rows
Dim CandReadDate As Date
Dim CandRowNextRead As String = DBNull.Value.ToString
If Not (CandRow("NEXT READ").ToString = DBNull.Value.ToString) Then
If Date.TryParse(CandRow("NEXT READ").ToString, CandReadDate) Then
CandRowNextRead = CandReadDate.ToString("dd/MM/yyyy")
End If
End If
Dim CandRowSending As String = TryCast(CandRow("SENDING"), String)
Dim CandRowNotes As String = TryCast(CandRow("NOTES"), String)
For Each NewRow As DataRow In dtUsers.Rows
If CandRow("SQ").ToString = NewRow("SQ").ToString Then
UserLocations.Add(NewRow("SQ").ToString)
End If
Next
For Each ProgressRow As DataRow In dtProgress.Rows
If CandRow("SQ").ToString = ProgressRow("SQ").ToString Then
Dim NextReadDate As Date
Dim ProgressRowNextRead As String = DBNull.Value.ToString
If Not (ProgressRow("NEXT READ").ToString = DBNull.Value.ToString) Then
If Date.TryParse(ProgressRow("NEXT READ").ToString, NextReadDate) Then
ProgressRowNextRead = NextReadDate.ToString("dd/MM/yyyy")
End If
End If
Dim ProgressRowSending As String = TryCast(ProgressRow("SENDING"), String)
Dim ProgressRowNotes As String = TryCast(ProgressRow("NOTES"), String)
If CandRow("SQ").ToString = ProgressRow("SQ").ToString Then
If CandRowSending <> ProgressRowSending Then
BeginInvoke(New UpdateDataTableDelegate(AddressOf UpdateDataTableSending), CandRow, ProgressRowSending)
End If
If CandRowNextRead <> ProgressRowNextRead Then
BeginInvoke(New UpdateDataTableDelegate(AddressOf UpdateDataTableNextRead), CandRow, ProgressRowNextRead)
End If
If CandRowNotes <> ProgressRowNotes Then
BeginInvoke(New UpdateDataTableDelegate(AddressOf UpdateDataTableNotes), CandRow, ProgressRowNotes)
End If
End If
End If
Next
Next
dgv.BeginInvoke(
New MethodInvoker(
Sub()
For Each dgv_row As DataGridViewRow In dgv.Rows
If UserLocations.Contains(dgv_row.Cells("SQ").Value.ToString) Then
dgv.DefaultCellStyle.BackColor = My.Settings.customRowHighlight
Else
dgv.DefaultCellStyle.BackColor = Nothing
End If
Next
End Sub))
Thread.Sleep(My.Settings.customRefreshRate * 1000)
Loop
End Sub
The subs that do the DataTable update are like this:
Private Delegate Sub UpdateDataTableDelegate(ByVal CandRow As DataRow, ByVal ProgressRow As String)
Private Sub UpdateDataTableSending(ByVal CandRow As DataRow, ByVal ProgressRowSending As String)
CandRow("SENDING") = ProgressRowSending
End Sub
I know this is not the best way to handle a multi-user environment but the nature of this work requires that all people can access and see the same data. I could force them to refresh regularly but that seems very intrusive.
The crashes only occur when this thread is running and the crashes are regular (and not instant) but I cannot seem to reproduce them and the application is very stable otherwise.
There must be some cross-threading issue but I can't work how when all of the updates to the DataTable or DataGridView are done via a BeginInvoke on the main UI thread.
EDIT: I've just realised that even though I am doing the queries and most of the heavy lifting in the background thread, the updates are stilled called on the main UI thread which would lock the thread. This would be particularly noticeable if there were a lot of updates... Because each one is called individually.
If the UI lock up was long enough, and the user was clicking on stuff, would this cause Windows to treat the application as unresponsive and crash it? If so, is there a better way I could handle these updates?
Any help with resolving this would be enormously appreciated.

Using Filewatcher for a progress bar with subdirectories, Wont Update Properly

I'm trying to copy Files from a local Computer to a Network device. I'm trying to get a Progress Bar working for the File copy, and got it working for a Single Directory with no Subdirectory:
Private Sub CopyPictures()
Try
If Not Directory.Exists(DestinationPath) Then
My.Computer.FileSystem.CreateDirectory(DestinationPath)
End If
Dim counterLocalFiles = My.Computer.FileSystem.GetFiles(SourcePath)
UpdateProgressBarMaximum1(CInt(counterLocalFiles.Count))
UpdateLabelText2(CStr(counterLocalFiles.Count)) 'is a label which shows copied X files of Label2 Files
fsw1 = New IO.FileSystemWatcher(DestinationPath)
fsw1.EnableRaisingEvents = True
My.Computer.FileSystem.CopyDirectory(SourcePath, DestinationPath)
GetSettingsFromFile()
Catch Exec As System.IO.IOException
Dim dr As DialogResult = MessageBox.Show("Some Random Error Code", "Exception Title", MessageBoxButtons.OKCancel)
If (Not DialogResult.OK = dr) Then
Exit Sub
Return
End If
End Try
End Sub
Private Sub fsw1_Created(sender As Object, e As FileSystemEventArgs) Handles fsw1.Created
Dim counterRemoteFiles = My.Computer.FileSystem.GetFiles(DestinationPath)
UpdateProgressBar1(CInt(counterRemoteFiles.Count))
UpdateLabelText1(CStr(counterRemoteFiles.Count))
End Sub
The Update ObjectX Subs are just invoke Functions since the CopyPictures is raised by a backgroundworker as well looking all like this one for example
Private Sub UpdateProgressBar1(Value As Int32)
If ProgressBar1.InvokeRequired Then
ProgressBar1.Invoke(New Action(Of Integer)(AddressOf UpdateProgressBar1), Value)
Else
'We are on the UI thread so update the control.
ProgressBar1.Value = Value
End If
End Sub
This code works perfectly fine for me, but I have to deal with SubDirectories which contain the Images, and the names of the subs are random so i cant predetermine them so I came up with slight changes:
The Counter is looking now like this:
Dim counterLocalFiles = System.IO.Directory.GetFiles(SourcePath, "*.jpg*", SearchOption.AllDirectories).Length
UpdateProgressBarMaximum1(CInt(counterLocalFiles))
UpdateLabelText2(CStr(counterLocalFiles))
And this:
Dim counterRemoteFiles = IO.Directory.GetFiles(DestinationPath, "*.jpg", SearchOption.AllDirectories).Length
UpdateProgressBar1(CInt(counterRemoteFiles))
UpdateLabelText1(CStr(counterRemoteFiles))
And I added:
fsw1.IncludeSubdirectories = True
Now the weired Problems started: It would properly count the file in the source Directory setting label2 to the correct amount of files in all subdirectories and then start copying. It would NOT update the Progressbar though in real time. It just updated it once when it was done with the first directory and just adding the amount of files to it which it contained. After that it completly stoppedd nored the second directory and didn't add that at all to the progressbar. What am I doing wrong here? I hope my english is fine, If you have any question or If I was not clear enough, please let me know. Thank you
You don't have an event consumer that triggers your progressbar update routine - you call it once when your filesystemwatcher is instantiated.
You need to declare an event that handles the copy event and fires off your progress update code. Because Filesystemwatcher cannot monitor network drives, you may want to declare an event that fires off your progress update method when the counterRemoteFiles count increments.
Turns out I just made a mistake with correctly putting the
fsw1.IncludeSubdirectories = True
I was setting it to true in the Form Editor instead of doing it in the code. Once i actually put that in the code after initialising the fsw, it would work just fine

Task is running and cannot be finished

Have strange behaviour in my task which is not finishing. I use this all the time but i suppose its because sub i am passing to it is iteracting with form - changing selection and refreshing some listbox probably therefore its stack there but i am not sure. Lets see the code:
This is the sub i want to be run in task:
Public Sub UnselectExistingConnectionsItems()
Dim SentenceId, SubSubKategorieId, SubSectionId As Integer
SubSectionId = CbSubSections.SelectedValue 'combobox
If WithSubSubkategorie = SubSubKategorieEnum.Without Then
SubSubKategorieId = 0
Else
SubSubKategorieId = CbSubSubKategorie.SelectedValue 'combobox
End If
Unselect:
For i As Integer = 0 To LB_Sentences.SelectedItems.Count - 1
Dim sKey As ListBoxItem
sKey = LB_Sentences.SelectedItems(i)
SentenceId = HtmlDescription.HtmlSentence.GetSentenceIdByName(sKey.Text)
If HtmlDescription.HtmlSubSubSections_Sentences.CheckIfConnectionAlreadyExist(SentenceId, SubSectionId, SubSubKategorieId) Then
sKey.IsSelected = False
LB_Sentences.Refresh()
GoTo Unselect
End If
Next
End Sub
i put it to Task like this:
Dim pic As New FrmCircularProgress(eCircularProgressType.Line)
Dim work As Task = Task.Factory.StartNew(Sub()
'--Run lenghty task UnselectExistingConnectionsItems()
'--Close form once done (on GUI thread)
pic.Invoke(New Action(Sub() pic.StopCircular()))
pic.Invoke(New Action(Sub() pic.Close()))
End Sub)
'--Show the form
pic.ShowDialog()
Task.WaitAll(work)
and FrmCircularProgress is just form ( i use it almost everywhere where i have to user wait and its working besides this particural case):
Public Class FrmCircularProgress
Sub New(progressType As DevComponents.DotNetBar.eCircularProgressType)
InitializeComponent()
CircularProgress1.ProgressBarType = progressType
StartCircular()
End Sub
Public Sub StartCircular()
Me.CircularProgress1.IsRunning = True
End Sub
Public Sub StopCircular()
Me.CircularProgress1.IsRunning = False
End Sub
End Class
what could be wrong? is it because procedure is interacting with listbox and combobxes? If so how to fix that, i read something about invoking listbox and comboboxes but have no idea how to fix that.
EDIT:
I think besides those lines:
sKey.IsSelected = False
LB_Sentences.Refresh()
I have to make those:
LB_Sentences.Invoke(Sub() sKey.IsSelected = False
End Sub)
LB_Sentences.Invoke(Sub() LB_Sentences.Refresh()
End Sub)
because i am in diffrent thread. Somehow i dont know how to convert those lines:
SubSectionId = CbSubSections.SelectedValue
SubSubKategorieId = CbSubSubKategorie.SelectedValue
probably loop also have to be invoked. Waiting your help.
There is a rule that says "The only thread that can modify a control in a window is the thread that created the window". Any other thread trying to modify something in the window will generate a cross-thread call exception.
So in your first edit you got it right, you have to invoke the functions.
However, this doesn't fix your problem of not finishing Task.
I believe that doing sKey.IsSelected = False does not unselect anything in your ListBox, therefore causing an infinite loop... Also that Goto statement is very bad programming habits and should not be used. There is always another solution that will make your code easier to debug/maintain/read...
ListBoxItem is not a type that exists in the .Net Framework. So either you created that class either it's something else (and I don't know what...)
What you can do to solve your problem is :
Get the indices of all selected items in a list
Run through your list, and check if they should be selected :
If they should be selected, do nothing
if they shouldn't, unselect them.
Which makes your code like this (and you remove that ugly Label and Goto that you don't want in your code)...
Public Sub UnselectExistingConnectionsItems()
Dim SentenceId, SubSubKategorieId, SubSectionId As Integer
SubSectionId = CbSubSections.SelectedValue 'combobox
If WithSubSubkategorie = SubSubKategorieEnum.Without Then
SubSubKategorieId = 0
Else
SubSubKategorieId = CbSubSubKategorie.SelectedValue 'combobox
End If
'We create an array to remind our initial selection
Dim sel = New Integer(LB_Sentences.SelectedItems.Count - 1) {}
LB_Sentences.SelectedIndices.CopyTo(sel, 0)
For i = 0 To sel.Length - 1
Dim sKey As ListBoxItem
'We get our selected item
sKey = LB_Sentences(sel(i))
SentenceId = HtmlDescription.HtmlSentence.GetSentenceIdByName(sKey.Text)
If HtmlDescription.HtmlSubSubSections_Sentences.CheckIfConnectionAlreadyExist(SentenceId, SubSectionId, SubSubKategorieId) Then
'We must remove it from the selection
LB_Sentences.Invoke(Sub() LB_Sentences.SelectedItems.Remove(sKey))
End If
Next
'We do the Refresh at the end so we gain some process time...
LB_Sentences.Invoke(Sub() LB_Sentences.Refresh())
End Sub

VB.NET/WMI - Real-Time Windows Service Monitoring?

So there's an application at my work that installs several Windows services to a server. As a side project, I've been asked to make a simple GUI that will list these services with a "light" (a picture box with a red or green dot) next to the name of each service. The idea is that in the event these services were to stop running, the "light" would change from green to red.
I have the GUI part built, and I can query a remote server's services, then compare it to an array of the ones I'm interested and set the "light" next to each service to green/red depending on the service state. The part I'm hung up on is how to monitor these services in real time? Currently, I just have the following code in the Form_Load event:
Dim myConnectionOptions As New System.Management.ConnectionOptions
With myConnectionOptions
.Impersonation = System.Management.ImpersonationLevel.Impersonate
.Authentication = System.Management.AuthenticationLevel.Packet
End With
Try
Dim myManagementScope As System.Management.ManagementScope
myManagementScope = New System.Management.ManagementScope("\\" & SERVERNAME & "\root\cimv2", myConnectionOptions)
myManagementScope.Connect()
Dim query As New Management.ObjectQuery("SELECT * FROM Win32_Service")
Dim searcher As New Management.ManagementObjectSearcher(myManagementScope, query)
Dim i As Integer = 0
For Each queryObj As Management.ManagementObject In searcher.Get()
For Each service As String In arrServices
If queryObj("DisplayName").Equals(service) Then
If queryObj("State").Equals("Stopped") Then
arrLights(i).Image = My.Resources.redlight
End If
i += 1
End If
Next
Next
Catch err As Management.ManagementException
MessageBox.Show("WMI query failed with the following error: " & err.Message)
Catch unauthorizedErr As System.UnauthorizedAccessException
MessageBox.Show("Authentication error: " & unauthorizedErr.Message)
End Try
Would a simple timer that executes this code repeatedly be the best approach, or is there a more elegant solution? I have a little experience in VB.NET and WMI, but none in any type of real-time monitoring activity like this.
First of all i would put it into a thread, that way even if your connection times out you dont freeze your UI, then i would use a custom wait timer not the built in one as cross threading can be a pain.
wait timer:
Public Sub Wait(ByVal wait_time As Integer)
Dim time As Date
time = Now.AddMilliseconds(wait_time)
Do While time > Now
Application.DoEvents()
Loop
End Sub
example of threading:
Private services_check As Thread
private sub form1_load(sender As System.Object, e As System.EventArgs) Handles MyBase.Load
services_check = new thread(AddressOf 'Current code in a public sub')
services_cheack.IsBackground = True
Services_check.start()
It may not be the most elegant solution but its how i would do it, as for your current code im sorry i dont know enough about remote connections to help you.