How to put received serial text into multiple text boxes? - vb.net

I am doing a serial communication project and would like to have the received string go into a text box based on which button was clicked to send the initial string and invoke a response.
The code for the ReceivedText is:
PrivateSub ReceivedText(ByVal [text] As String)
Button1.Clear()
Button2.Clear()
If Button1.InvokeRequired Then
RichTextBox1.text = [text].Trim("!")
End If
If Button2.InvokeRequired Then
RichTextBox2.Text = [text].Trim("!")
End If
EndSub
This just results in the received string going into both of the boxes instead of one or the other.
Is there any way to get the text to go in the appropriate box?

The key to remember is that .Net treats all serial comm as threads. Let me give you a simple example of updating textboxes from one of my programs that reads data from a scale.
Private Sub ComScale_DataReceived(sender As System.Object, e As System.IO.Ports.SerialDataReceivedEventArgs) Handles ComScale.DataReceived
If ComScale.IsOpen Then
Try
' read entire string until .Newline
readScaleBuffer = ComScale.ReadLine()
'data to UI thread because you can't update the GUI here
Me.BeginInvoke(New EventHandler(AddressOf DoScaleUpdate))
Catch ex As Exception : err(ex.ToString)
End Try
End If
End Sub
You'll note a routine DoScaleUpdate is invoked which does the GUI stuff:
Public Sub DoScaleUpdate(ByVal sender As Object, ByVal e As System.EventArgs)
Try
'getAveryWgt just parses what was read into something like this {"20.90", "LB", "GROSS"}
Dim rst() As String = getAveryWgt(readScaleBuffer)
txtWgt.Text = rst(0)
txtUom.Text = rst(1)
txttype.Text = rst(2)
Catch ex As Exception : err(ex.ToString)
End Try
End Sub
You CAN make it much more complicated if you choose (see post #15 of this thread for an example) but this should be enough to do what you need.

Related

Async Download file updating the gui

I am downloading a file using webclient handling DownloadProgressChanged and DownloadFileCompleted.
My code is working whenever I am not updating the gui with percentage,b but In case I am going to update just a single label with it, then it kind of freezing in random situations. Doesn't matter if the file is little or big.
Public WithEvents mclient As New WebClient
Private Sub Download()
Dim filepath As String = (filepath..)
mclient.Encoding = Encoding.UTF8
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12
mclient.Headers.Add(HttpRequestHeader.UserAgent, "")
mclient.DownloadFileAsync(New Uri(link), filepath)
End Sub
Private Sub mClient_DownloadProgressChanged(sender As Object, e As DownloadProgressChangedEventArgs) Handles mclient.DownloadProgressChanged
Try
'file website missing Content-Length, so getting the real size parsing html (real size=label7)
label1.text= ((e.BytesReceived) / 1048576).ToString("0.00") & "/" & Label7.Text
Catch ex As Exception
End Try
End Sub
Private Sub mClient_DownloadFileCompleted(sender As Object, e As AsyncCompletedEventArgs) Handles mclient.DownloadFileCompleted
Try
label2.text="Download ended"
Catch ex As Exception
End Try
End Sub
How can I keep label1.text updated without using it inside of DownloadProgressChanged?
If I use a variable on it, where can I keep it updated? I don't want to use a timer though..

Barcode scanning to a listbox checking for duplicates

Good day!
I want to add some strings from a barcode scanner, captured in a text box, to a list box, and, before adding it, to check if the specific string hasn't been already added. So I have a text box called txtWO which captures what the reader scans and a list box called lstScanBOM to which I add the text box string if the item is not already added. The problem is, that whatever I do, only after the specific string is added twice the checking for duplicate entry starts to work. In other words I scan the same string twice, it added it, and then when I scan the third time only it throws the message with the error saying it is a duplicate. I don't understand why is doing this. The code is below:
Private Sub frmValidareFIP_Load(sender As Object, e As EventArgs) Handles MyBase.Load
If txtWO.Focused = False Then
txtWO.Select()
End If
End Sub
Private Sub AddUnique(StringToAdd As String)
If lstScanBom.Items.Contains(StringToAdd) = True Then
MsgBox("Articol duplicat!", vbOKOnly)
Else
'it does not exist, so add it..
lstScanBom.Items.Add(StringToAdd)
End If
End Sub
Private Sub txtWO_KeyDown(ByVal sender As Object,ByVal e As System.Windows.Forms.KeyEventArgs) Handles txtWO.KeyDown
If e.KeyCode = Keys.Enter Then
Dim barcode As String
barcode = txtWO.Text
AddUnique(barcode)
txtWO.Clear()
txtWO.Focus()
End If
End Sub
IMO Try listing the data outside of the ListBox. I can't see why it isn't working, maybe we need a third pair of eyes to see it!?
Try adding a list (of string) as a Private within the form, populate this as your user scans, and check the duplicate there..
This is definately not the best solution, but I'm sure it will help!
Private List_Barcodes As List(Of String)
Private Sub frmValidareFIP_Load(sender As Object, e As EventArgs) Handles MyBase.Load
List_Barcodes = New List(Of String)
'You can also populate this list on load, if you have a stored cahce of previous scanned barcodes?
'List_Barcodes.Add("0123456")
'List_Barcodes.Add("4567890")
'...etc
If txtWO.Focused = False Then
txtWO.Select()
End If
End Sub
Private Sub AddUnique(StringToAdd As String)
If List_Barcodes.Contains(StringToAdd) Then
MsgBox("Articol duplicat!", vbOKOnly)
Else
'Place into dynamic list
List_Barcodes.Add(StringToAdd)
'and Place into your listbox
lstScanBom.Items.Add(StringToAdd)
End If
End Sub
Private Sub txtWO_KeyDown(ByVal sender As Object, ByVal e As System.Windows.Forms.KeyEventArgs) Handles txtWO.KeyDown
If e.KeyCode = Keys.Enter Then
Dim barcode As String
barcode = txtWO.Text
AddUnique(barcode)
txtWO.Clear()
txtWO.Focus()
End If
End Sub
Your barcode reader is returning <carriage return><line feed> as enter. Your code catches the enter key (carriage return = 13), but leaves the line feed (10) character. So the next time you scan something it will start with a line feed. The two strings in your example are different because the first is "58335001" and the second is "<line feed>58335001". The third one is "<line feed>58335001", which is a duplicate of the second.
One way to fix this is to trim your string.
Private Sub txtWO_KeyDown(ByVal sender As Object, ByVal e As System.Windows.Forms.KeyEventArgs) Handles txtWO.KeyDown
If e.KeyCode = Keys.Enter Then
Dim barcode As String
'Add the .Trim() to remove the leading <line feed> character
barcode = txtWO.Text.Trim()
AddUnique(barcode)
txtWO.Clear()
txtWO.Focus()
End If
End Sub
The most simple decision is ONLY to make your textBox control txtWO NOT multiline
And that's enough! Your code will work correctly!

Raise Event when a Function return True

Part of a program I am modifying involves communicating through a serial port using a proprietary library. Unfortunately, this library does not have the same SerialPort.DataReceived event that the System.IO.Ports namespace contains. In fact, it has no events whatsoever, however it does have two functions that can probably be used similarly:
Port.WaitForData(int time)
This function waits the given amount of time to recieve some previously specified strings over the port. It returns 1 for yes, received string, or 0 for no, did not recieve string, timed out.
Port.IsReceiveBufferEmpty()
This function returns a boolean of yes, the receive buffer is empty or no, the receive buffer contains data.
It seems to me I will have to create some thread to be continuously looping whenever the port is opened and do one of these two things:
For every loop, call WaitForData(some big number) with the specified strings it is looking for set to "", or vbCrLf, or something else that I can confirm it will recieve everytime data is sent. If it finds smoething, read it and write to a textbox. If WaitForData doesn't find anything, loop again.
For every loop, call IsReceiveBufferEmpty(), and if it isn't, read it and write to a textbox.
What the best way to go about implementing this? The first options seems potentially more efficient to me, although I know next to nothing about how these method work under the hood. Obviously I want to keep my form responsive when doing this, so how should I go about continuously looping without freezing the form but being able to read any incoming data?
Thanks for your help.
Perhaps not the most elegant solution, but you could use a BackgroundWorker to do the IO. e.g. something like this pseudo-code:
Public Class MyForm
Private _port As ProprietaryIOLibrary
Private WithEvents Worker As System.ComponentModel.BackgroundWorker
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles Me.Load
_port = New ProprietaryIOLibrary()
Worker = New System.ComponentModel.BackgroundWorker()
Worker.WorkerReportsProgress = True
Worker.WorkerSupportsCancellation = True
Worker.RunWorkerAsync()
End Sub
Private Sub ButtonCancel_Click(sender As Object, e As EventArgs) Handles ButtonCancel.Click
Worker.CancelAsync()
End Sub
Private Sub Worker_DoWork(sender As Object, e As DoWorkEventArgs) Handles Worker.DoWork
Do
If _port.WaitForData(1000) Then ' Adjust timeout depending on cancel responsiveness?
Dim data As String = _port.ReadDataAsString() ' ?
' Trigger the ProgressChanged event, passing the data
Worker.ReportProgress(0, data)
End If
If Worker.CancellationPending Then
Exit Do
End If
Loop
End Sub
Private Sub Worker_ProgressChanged(sender As Object, e As ProgressChangedEventArgs) Handles Worker.ProgressChanged
' Update the UI with the data received
' ProgressChanged is called on the UI thread, so no need to Invoke
Dim data As String = DirectCast(e.UserState, String)
TextBox1.Text &= data & vbCrLf
End Sub
Private Sub Worker_RunWorkerCompleted(sender As Object, e As RunWorkerCompletedEventArgs) Handles Worker.RunWorkerCompleted
TextBox1.Text &= "Complete!"
End Sub
End Class

Vb.net - Cross-threading exception by closing a form

I'm working on an application to read something from a serial port (COMM-port).
In short, it works like this: when you work in a bar or restaurant, before you can enter something in the register, you have to scan a sort of card. If this card returns a good number, you can enter something.
So, there has to be a form that listens to the serial port and checks whether someone scans a card and if it's a card with good rights.
If the person has the good rights, the form can be closed and another form is called.
Now, in code:
Here, the MenuForm is loaded (the form that has to be accesible after the correct code was read). I call the frmWaiterKey to show up.
Private Sub frmMenu_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
Dim oForm As frmWaiterKey = New frmWaiterKey()
oForm.ShowDialog()
End Sub
The code of the class frmWaiterKey:
Private Sub frmWaiterKey_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
nameArray = SerialPort.GetPortNames
OpenComPort()
AddHandler myComPort.DataReceived, SerialDataReceivedEventHandler1
End Sub
Sub OpenComPort()
Try
' Get the selected COM port's name
' from the combo box.
If Not myComPort.IsOpen Then
myComPort.PortName = _
nameArray(0).ToString()
' Get the selected bit rate from the combo box.
myComPort.BaudRate = CInt(9600)
' Set other port parameters.
myComPort.Parity = Parity.None
myComPort.DataBits = 8
myComPort.StopBits = StopBits.One
myComPort.Handshake = Handshake.None
'myComPort.ReadTimeout = 3000
'myComPort.WriteTimeout = 5000
' Open the port.
myComPort.Open()
End If
Catch ex As InvalidOperationException
MessageBox.Show(ex.Message)
Catch ex As UnauthorizedAccessException
MessageBox.Show(ex.Message)
Catch ex As System.IO.IOException
MessageBox.Show(ex.Message)
End Try
End Sub
Sub CloseComPort()
Using myComPort
If (Not (myComPort Is Nothing)) Then
' The COM port exists.
If myComPort.IsOpen Then
' Wait for the transmit buffer to empty.
Do While (myComPort.BytesToWrite > 0)
Loop
End If
End If
End Using
End Sub
Private SerialDataReceivedEventHandler1 As New SerialDataReceivedEventHandler(AddressOf DataReceived)
' Specify the routine that runs when
' a DataReceived event occurs at myComPort.
' This routine runs when data arrives at myComPort.
Friend Sub DataReceived(ByVal sender As Object, ByVal e As SerialDataReceivedEventArgs)
Dim newReceivedData As String
' Get data from the COM port.
newReceivedData = myComPort.ReadExisting
newReceivedData = newReceivedData.Trim()
MsgBox(newReceivedData)
If newReceivedData.Equals("00150324294764") Then
CloseComPort()
Me.Close()
End If
End Sub
I get an error in the last line: Me.Close()
I get the point: I call the form frmWaiterKey from the frmMenu and can't close it here...
But I have no idea how to solve this problem.
I hope someone can help me or tell me what I'm doing wrong.
First, you need to make a method like this:
Private Sub CloseMe()
If Me.InvokeRequired Then
Me.Invoke(New MethodInvoker(AddressOf CloseMe))
Exit Sub
End If
Me.Close()
End Sub
Then, close your form by calling that method, like this:
If newReceivedData.Equals("00150324294764") Then
CloseComPort()
CloseMe()
End If
The reason this is necessary is because all UI activity in WinForms must be performed from the same thread. Since the DataReceived method is being called from another thread, it must get back onto the UI thread before it can close the form. The InvokeRequired property returns true if you are on any thread other than the UI thread, and the Invoke method invokes the given method from the UI thread.

How to ignore windows events when in a child form called from that event handler?

My specific question is: How to ignore windows events when in a child form called from that event handler?
Some context: My application captures a fingerprint, and puts up the next form (secondForm) when a print is recognized against the database. I want to ignore any prints they put in while their secondForm is up. The trouble is that when people press the print multiple times, or while their secondForm is up, then the events are queued until after the secondForm closes, so I get multiple calls to the event handler for that person.. I've tried so many ways around this, including calling the routine that open secondForm as a delegate(is that even appropriate), putting a global boolean in the event handler, etc. If I disable fingerprint capture during the oncomplete eventhandler, my form never shows up.
Am I missing something obvious to you here? Much gratitude for any ideas...
Imports DPFP.Capture ' DigitalPersona fingerprint reader library
' Simple example to capture a fingerprint from DigitalPersona fingerprint reader.
Public Class SimpleFP
Implements DPFP.Capture.EventHandler
Private mIgnore As Boolean
Public eventHandlerComplete As DPFP.Capture.EventHandler
Public WithEvents mCapture As DPFP.Capture.Capture
Private Sub WaitForFPrint_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
Try
' set up finger print capture
mCapture = New DPFP.Capture.Capture
mCapture.EventHandler = Me
mCapture.StartCapture()
Catch ex As Exception
MsgBox("Problem starting fingerprint reader: " & ex.Message)
End Try
End Sub
Public Sub FPOnComplete(ByVal Capture As Object, ByVal sernum As String, ByVal sample As DPFP.Sample) _
Implements DPFP.Capture.EventHandler.OnComplete
'mCapture.StopCapture()
'This is what I want to do, but when I leave this in, my secondForm won't stay up
Dim s As String = displaySecondScreen(sample)
showStatus(s)
'mCapture.StartCapture()
'This is what I want to do, but when I leave this in, my secondForm won't stay up
End Sub
' Put up second form. This is called from the fingerprint OnComplete event handler.
Private Function displaySecondScreen(ByVal sample As DPFP.Sample) As String
Dim status As String = "OK"
Try
Dim x As DPFP.FeatureSet
x = extractFeatures(sample)
' This is where I match the fp in my code, but I removed here to simplify
If True Then
Dim frm As New frmSecondForm
frm.ShowDialog(Me)
frm.Dispose()
Else
' No fingerprint match - normal case
status = "Fingerprint not recognized"
End If
Catch ex As Exception
status = "Exception during fingerprint verification: " & ex.Message
End Try
Return status
End Function
' Made rudimentary for this example, but I thought some person new to dpfp may be able to use this
Private Function extractFeatures(ByVal sample As DPFP.Sample) As DPFP.FeatureSet
Dim extractor As New DPFP.Processing.FeatureExtraction()
Dim feedback As New DPFP.Capture.CaptureFeedback()
Dim features As New DPFP.FeatureSet()
extractor.CreateFeatureSet(sample, DPFP.Processing.DataPurpose.Verification, feedback, features)
If feedback = DPFP.Capture.CaptureFeedback.Good Then
End If
Return features
End Function
Private Sub debugOut(ByVal s As String)
Console.WriteLine(s)
End Sub
' Display status on first form
Delegate Sub showStatusCallback(ByVal s As String)
Private Sub showStatus(ByVal s As String)
If lblStatus.InvokeRequired Then
Dim d As New showStatusCallback(AddressOf showStatus)
Me.Invoke(d, New Object() {s})
Else
lblStatus.Text = s
End If
End Sub
' ... followed by other implements dpfp eventhandlers not in use....
' ....
End Class