Using Background Worker VB.NET - vb.net

I have to show many transaction on any table. It takes so much long time to process.
I want to use a background process in visual basic 2010, but it always give an error message like this "Cross thread operation detected". I have try so many ways that i found in the internet, but still can't find what the problem is.
Please help me about how to fix this problem.
This is my code :
Private Sub CHK_A_CheckedChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles CHK_A.CheckedChanged
Disabled()
P_Panel.Visible = True
B_Worker.RunWorkerAsync()
End Sub
Private Sub BTN_Search_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles BTN_Search.Click
USP_Select_Registration()
End Sub
Private Sub B_Worker_DoWork(ByVal sender As System.Object, ByVal e As System.ComponentModel.DoWorkEventArgs) Handles B_Worker.DoWork
Threading.Thread.Sleep(25)
USP_Select_Registration()
End Sub
Private Sub B_Worker_RunWorkerCompleted(ByVal sender As Object, ByVal e As System.ComponentModel.RunWorkerCompletedEventArgs) Handles B_Worker.RunWorkerCompleted
P_Panel.Visible = False
End Sub
This one is my store procedure
Public Sub USP_Select_Registration()
Dim Con As New SqlConnection(SQLCon)
Try
Con.Open()
Dim Cmd As New SqlCommand("USP_Select_Registration", Con)
Cmd.CommandType = CommandType.StoredProcedure
Cmd.Parameters.Add("#Check", SqlDbType.Bit).Value = CHK_A.EditValue
Cmd.Parameters.Add("#Month", SqlDbType.Int).Value = CBO_Month.SelectedIndex + 1
Cmd.Parameters.Add("#Year", SqlDbType.Int).Value = SPE_Year.EditValue
Cmd.Parameters.Add("#Search", SqlDbType.VarChar, 150).Value = TXT_Search.Text
DS_Registration.Tables("MST_Registration").Clear()
Dim Adt As New SqlDataAdapter(Cmd)
Adt.Fill(DS_Registration.Tables("MST_Registration"))
Catch ex As Exception
MsgBox(ex.Message, MsgBoxStyle.Critical, "Error")
Finally
Con.Close()
End Try
End Sub

I think you get error because inside of USP_Select_Registration you trying to read values from form control's. Which was created by another thread.
RunWorkComleted executes in the same thread where Backgroundworker was created, thats why code P_Panel.Visible = False will executes normally.
But DoWork executes on the another thread.
And when you tried to access some forms controls to read values
TXT_Search.Text - it raise error
You can pass you searching parameters to BackgroundWorker, but you need to add parameters to USP_Select_Registration function.
For example:
Private Sub USP_Select_Registration(searchText as String)
'Your code here
End Sub
Then where you start BackgroundWorker:
B_Worker.RunWorkerAsync(TXT_Search.Text)
And
Private Sub B_Worker_DoWork(ByVal sender As Object,
ByVal e As DoWorkEventArgs) Handles B_Worker.DoWork
Threading.Thread.Sleep(25)
USP_Select_Registration(e.Argument)
End Sub
In your case you need to pass more then one parameter.
So you can create some structure/class or whatever object where you can keep all needed values. And pass that object to RunWorkerAsync

Related

SQL table only updating on a specific click event, not the other

In code shown below, clicking on the btSave will cause the table to be updated, assuming that some data in vendmast has been changed. However, clicking on the label lblMyExit will run the code but it will not recognize that vendmast should be updated. Am very perplexed. This is the first question I have ever asked. Hope I am doing it right. Dennis
Private Sub btnSave_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnSave.Click, _
lblMyExit.Click
CmdBldr.DataAdapter = DA
CMD.CommandText = SQVEND
DA.SelectCommand = CMD
CmdBldr.DataAdapter = DA
CmdBldr.RefreshSchema()
DS.Tables("vendmast").Rows(0).EndEdit()
Try
DA.Update(DS.Tables("vendmast"))
DS.Tables("vendmast").AcceptChanges()
Catch
Debug.Print("ERROR")
End Try
End Sub
A cleaner way to do this is to put the update code in a separate method and call it from both event handlers. Six months from now it will be much easier to figure out what is going on when the label is clicked. As to why your code isn't working, is the lblMyExit.Click declared With Events in the designer code?
I don't have enough information to comment on the actual update code but be aware that database objects usually need to be disposed. It is often a bad idea to declare them outside the method where they are used. The language provides Using...End Using blocks to help you manage these objects.
Private Sub btnSave_Click(ByVal sender As Object, ByVal e As EventArgs) Handles btnSave.Click
UpdateDatabase()
End Sub
Private Sub lblMyExit_Click(sender As Object, e As EventArgs) Handles lblMyExit.Click
UpdateDatabase()
End Sub
Private Sub UpdateDatabase()
CMD.CommandText = SQVEND
DA.SelectCommand = CMD
CmdBldr.DataAdapter = DA
CmdBldr.RefreshSchema()
DS.Tables("vendmast").Rows(0).EndEdit()
Try
DA.Update(DS.Tables("vendmast"))
DS.Tables("vendmast").AcceptChanges()
Catch
Debug.Print("ERROR")
End Try
End Sub

ManagementEventWatcher character limit?

I'm trying to use eventwathcer but its not working with 11 characters process name (AbcdEfghIII.exe). If I write 10 characters process name (AbcdEfgIII.exe) its working. Is there character limit or am I doing something wrong? Here is my code:
Imports System.Management
Public Class Form1
Dim WithEvents StopWatch As New ManagementEventWatcher(New WqlEventQuery("SELECT * FROM Win32_ProcessStopTrace"))
Private Sub StopWatch_EventArrived(ByVal sender As Object, ByVal e As System.Management.EventArrivedEventArgs)
If e.NewEvent.Properties("ProcessName").Value.ToString = "AbcdEfghIII.exe" Then
MsgBox("Closed")
End If
End Sub
Private Sub Form1_Closing(ByVal sender As Object, ByVal e As System.ComponentModel.CancelEventArgs) Handles MyBase.Closing
StopWatch.Stop()
RemoveHandler StopWatch.EventArrived, AddressOf StopWatch_EventArrived
End Sub
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Try
AddHandler StopWatch.EventArrived, AddressOf StopWatch_EventArrived
StopWatch.Start()
Catch g As Exception
MsgBox("Please, run as admin.", MsgBoxStyle.Critical, "Error")
Me.Close()
End Try
End Sub
End Class
As there doesn't seem to be a way to make this work properly, I suppose you'll just have to use a hacky workaround: Checking if the process name starts with Customization.
Doing so will of course make it react to processes like CustomizationWHATEVER.exe, but since the code you're using for some reason doesn't return the full name there is no other way if you want it to work.
Using your initial code:
If check.StartsWith("Customization", StringComparison.OrdinalIgnoreCase) Then
MessageBox.Show("Closed")
End If
PREVIOUS ANSWER:
Here is a workaround:
Get the ProcessID field instead of ProcessName, and use that together with Process.GetProcessById() to get a .NET friendly Process class instance (from which you can extract the name).
Private Sub StopWatch_EventArrived(ByVal sender As Object, ByVal e As System.Management.EventArrivedEventArgs)
Dim PID As Integer = CType(CType(e.NewEvent.Properties("ProcessID").Value, UInteger) And Integer.MaxValue, Integer)
Dim p As Process = Process.GetProcessById(PID)
'NOTE: The ".exe" part is excluded in Process.ProcessName.
If String.Equals(p.ProcessName, "Customization", StringComparison.OrdinalIgnoreCase) Then
MessageBox.Show("Closed")
End If
End Sub

VB.NET how to populate a ComboBox in an UserControl from a Form

I have an UserControl where data from MySQL table loads in a ComboBox while it is loading.
Private Sub LoadFeeGroup()
Try
OpenConnection()
qry = "SELECT GroupId, GroupName FROM master_fee_group WHERE Stat='1' ORDER BY GroupName ASC"
Dim da As New MySqlDataAdapter(qry, conn)
Dim ds As New DataSet
da.Fill(ds, "master_fee_group")
With CmbGroup
.DataSource = ds.Tables("master_fee_group")
.DisplayMember = "GroupName"
.ValueMember = "GroupId"
End With
If CmbGroup.Items.Count > 0 Then
CmbGroup.SelectedIndex = 0
End If
Catch ex As Exception
MsgBox(ex.Message)
Finally
CloseConnection()
End Try
End Sub
And this Subroutine is being called when the UserControl is being loaded.
Private Sub AdmissionFeeUc_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
LoadFeeGroup()
End Sub
Now user can add any Fee Group Name (if not added previously) by opening a form.
Now I want to call this LoadFeeGroup() subroutine from that form so that user can see the added Fee Group Name in the ComboBox of the UserControl after closing the form.
Something like...
Private Sub FormFeeGroup_FormClosing(ByVal sender As System.Object, ByVal e As System.Windows.Forms.FormClosingEventArgs) Handles MyBase.FormClosing
' Code for calling the UserControl subroutine..
End Sub
I have tried to call the Subroutine like below,
but failed.
How can I do that ?
UPDATE
I have added a button in the UserControl.
Private Sub BtnNewGroup_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles BtnNewGroup.Click
Dim frmFeeGroup As New FormFeeGroup
Dim dlgres As DialogResult = frmFeeGroup.ShowDialog()
If DlgRes <> DialogResult.OK Then
Return
Else
LoadFeeGroup()
End If
End Sub
And in the FormClosing event
Private Sub FormFeeGroup_FormClosing(ByVal sender As Object, ByVal e As System.Windows.Forms.FormClosingEventArgs) Handles Me.FormClosing
If Me.DialogResult <> Windows.Forms.DialogResult.OK Then
Return
Else
'nothing to do
End If
End Sub
And in the Close button in the form,
Private Sub BtnClose_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles BtnClose.Click
Me.DialogResult = Windows.Forms.DialogResult.OK
Me.Close()
End Sub
My purpose is Partially served. Partially because, If I open the Form from the menu, the ComboBox is not updated.
You could create your own constructor for the form where you must pass a user control instance to it. That way you cannot create an instance of the form without specifying which user control it should be "tied" to.
For instance, put this in your form's code:
Public Property TargetFeeUc As AdmissionFeeUc
Public Sub New(ByVal FeeUc As AdmissionFeeUc)
InitializeComponent() 'Must be called before anything else.
Me.TargetFeeUc = FeeUc
End Sub
Then to create a new form instance you'll always be forced to give it an AdmissionFeeUc control.
'If written inside the user control's code:
Dim frmFeeGroup As New FormFeeGroup(Me)
'If written somewhere else:
Dim frmFeeGroup As New FormFeeGroup(<user control instance here>)
'...for example:
Dim frmFeeGroup As New FormFeeGroup(AdmissionFeeUc1)
And whenever you want to update the user control from your FormFeeGroup form you just need to call:
TargetFeeUc.LoadFeeGroup()
EDIT:
To get the user control instance if it was created dynamically you have to set the control's Name property when creating it, then you can reference it via:
<target control or form>.Controls("<user control name here>")
'For example:
Me.Controls("MyFeeUc")
'Or for sub-controls:
Me.Panel1.Controls("MyFeeUc")

how to run a function/sub after loading the form window in VB?

I have a function that gets User ID from USB badge reader, used to log in an application.
when I run the app, the log in window does not appear until I swipe the tag.
I need to know if it`s possible to load the windows, then to start running the function that gets the data from the USB.
Thanks :)
Private Sub SerialPort1_DataReceived()
'Threading.Thread.SpinWait(1000)
OpenPort()
If SerialPort1.IsOpen() Then
byteEnd = SerialPort1.NewLine.ToCharArray
'read entire string until .Newline
readBuffer = SerialPort1.ReadLine()
readBuffer = readBuffer.Remove(0, 1)
readBuffer = readBuffer.Remove(8, 1)
WWIDTextBox.AppendText(readBuffer)
End If
End Sub
Private Sub Form1_Activated(ByVal sender As Object, _
ByVal e As System.EventArgs) Handles MyBase.Activated
SerialPort1_DataReceived()
End Sub
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
'SerialPort1_DataReceived()
End Sub
The problem is that you are calling the ReadLine method, which is a blocking (synchronous) method. In other words, when you call it, the method does not return the value until it has the value to return. Because of that, it stops execution on the current thread until a complete line is read (when the badge is swiped). Since you are on the UI thread when you call it, it will lock up the UI until the badge is swiped.
Instead of calling your SerialPort1_DataReceived method from the UI thread, you can do the work from a different thread. The easiest way to do that is to drag a BackgroundWorker component onto your form in the designer. Then you can add code like this:
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
BackgroundWorker1.RunWorkerAsync()
End Sub
Private Sub BackgroundWorker1_DoWork(sender As Object, e As DoWorkEventArgs) Handles BackgroundWorker1.DoWork
OpenPort()
If SerialPort1.IsOpen() Then
byteEnd = SerialPort1.NewLine.ToCharArray
Dim readBuffer As String = SerialPort1.ReadLine()
readBuffer = readBuffer.Remove(0, 1)
readBuffer = readBuffer.Remove(8, 1)
e.Result = readBuffer
End If
End Sub
Private Sub BackgroundWorker1_RunWorkerCompleted(sender As Object, e As RunWorkerCompletedEventArgs) Handles BackgroundWorker1.RunWorkerCompleted
WWIDTextBox.AppendText(CStr(e.Result))
End Sub
Working on VS2013, I came across the same issue, I needed to to a datagridview refresh (colors in the gridrows). This worked for me.
Sub MyForm_VisibleChanged(sender As Object, e As EventArgs) Handles Me.VisibleChanged
If Me.Visible Then
'do action...
End If
End Sub
Try Form Activated Event
Private Sub Form1_Activated(ByVal sender As Object, _
ByVal e As System.EventArgs) Handles MyBase.Activated
'Call your function here
End Sub
It call the function After the Form Loads...
Private Sub loadCombo()
Dim sqlconn As New OleDb.OleDbConnection
Dim connString As String
connString = ""
Dim access As String
access = "select slno from atable"
Dim DataTab As New DataTable
Dim DataAdap As New OleDbDataAdapter(access, connString)
DataAdap.Fill(DataTab)
ComboBox1.DataSource = DataTab
ComboBox1.DisplayMember = "slno"
End Sub

VB.Net - Updating progress bar from background worker

I am trying to build a log parser in VB.Net that will take IIS logs and insert them into a database. Having never really made a full out desktop application, I'm hitting a number of stumbling blocks, so please forgive me if my questions a very uninformed; I'm learning to walk while running.
The code that I'm working with looks like this:
Protected Overrides Sub OnLoad(ByVal e As System.EventArgs)
MyBase.OnLoad(e)
BackgroundWorker1.RunWorkerAsync()
End Sub
Private Sub BackgroundWorker1_DoWork(ByVal sender As System.Object, ByVal e As DoWorkEventArgs) Handles BackgroundWorker1.DoWork
Dim logfile = "C:\ex111124.log"
Dim FileLength As Long = New System.IO.FileInfo(logfile).Length
logFileLabel.Text = logfile
Dim objReader As New System.IO.StreamReader(logfile)
Do While objReader.Peek() <> -1
OngoingLog.AppendText(objReader.ReadLine)
'BackgroundWorker1.ReportProgress(e.percentProgress)
Loop()
objReader.Close()
objReader = Nothing
End Sub
Private Sub BackgroundWorker1_ProgressChanged(ByVal sender As Object, ByVal e As ProgressChangedEventArgs) Handles BackgroundWorker1.ProgressChanged
'Me.crunchingProgress.Value = e.ProgressPercentage
End Sub
Private Sub BackgroundWorker1_RunWorkerCompleted(ByVal sender As Object, ByVal e As RunWorkerCompletedEventArgs) Handles BackgroundWorker1.RunWorkerCompleted
Close()
End Sub
So the function works, when this window is opened it starts to read the log file and updates a textbox with all of the rows currently read, but I also want it to update a progress bar in my main thread called crunchingProgress.
Any help would be greatly appreciated.
This should do the trick:
Private Sub BackgroundWorker1_ProgressChanged(ByVal sender As Object, ByVal e As ProgressChangedEventArgs) Handles BackgroundWorker1.ProgressChanged
Invoke(Sub()
Me.crunchingProgress.Value = e.ProgressPercentage
End Sub)
End Sub
You don't set the BackgroundWorker to report progress (WorkerReportsProgress)
BackgroundWorker1.WorkerReportsProgress = True
BackgroundWorker1.RunWorkerAsync()
Now your BackgroundWorker1_ProgressChanged will be called in the context of the UI Thread and you can set the progressbar value
Of course, when you call ReportProgress to raise the ProgressChanged event, you need to pass the percentage of work done so far.
Using objReader As New System.IO.StreamReader(logfile)
Do While objReader.Peek() <> -1
Dim line = objReader.ReadLine()
OngoingLog.AppendText(line)
Dim pct = Convert.ToInt32((100 * line.Length) / FileLength )
BackgroundWorker1.ReportProgress(pct)
Loop
End Using