I have an application with 8 threads. The main of the app is to display realtime information from 4 outputs and also write the information to a file.
Now, I do not know very much in the domain of multi-threading, but I have succeeded in creating the 4 threads,and if I do not log the info into the rich text boxes, they work ok, they write the info into the files.
My question is: Can I display real time info, from each output on the same form (MainForm)
Outputs work independent one to each other
Below is the code I use to log the info into the richtextbox:
Public Sub LogInBoxOutput2(ByVal [RichTextBox] As RichTextBox, textColor As Color, ByVal [text] As String, Optional logToFile As Boolean = False)
Dim textToWrite As String = "[" & Now.ToString("dd:MM:yyyy HH:mm:ss.fff") & "] - " & [text]
If logNodesValues Then
Me.InvokeIfRequired(Sub()
If textColor = Nothing Then
[RichTextBox].SelectionColor = Color.Black
Else
[RichTextBox].SelectionColor = textColor
End If
[RichTextBox].SelectedText = textToWrite
If scrollToBottom Then
[RichTextBox].Select([RichTextBox].Text.Length - 1, 0)
[RichTextBox].ScrollToCaret()
End If
[RichTextBox].AppendText(vbCrLf)
If logToFile Then
writeToFileQueue2.Enqueue(textToWrite & vbCrLf)
End If
End Sub)
Else
writeToFileQueue2.Enqueue(textToWrite & vbCrLf)
End If
End Sub
<Extension()>
Public Sub InvokeIfRequired(ByVal Control As Control, ByVal Method As Action)
If Control.InvokeRequired Then
Control.Invoke(Method)
Else
Method.Invoke()
End If
End Sub
Do you have any ideas, what could I do? Is there something like in ASP.NET where you could refresh only a frame/part of the page (meaning to invoke only a part of the mainform)?
thank you
Instead of invoking you should queue all the messages to be outputted to the UI as well in order to give the UI and your processor some rest. Then use a System.Windows.Forms.Timer with an Interval of 1 to empty that queue as often as possible (approximately every 50 ms) and write the messages into each RichTextBox.
You can define your own data type that holds the necessary information for each message:
Public Structure OutputMessage
Public Color As Color?
Public Text As String
Public [RichTextBox] As RichTextBox
Public ScrollToBottom As Boolean
Public Sub New(ByVal Text As String, ByVal Color As Color?, ByVal ScrollToBottom As Boolean, ByVal [RichTextBox] As RichTextBox)
Me.Text = Text
Me.Color = Color
Me.ScrollToBottom = ScrollToBottom
Me.RichTextBox = [RichTextBox]
End Sub
End Structure
Then in your form:
Private MessageQueue As New ConcurrentQueue(Of OutputMessage)
Dim WithEvents UpdateTimer As New Timer With {.Interval = 1, .Enabled = True}
Private Sub UpdateTimer_Tick(sender As Object, e As EventArgs) Handles UpdateTimer.Tick
Dim Message As OutputMessage
While MessageQueue.TryDequeue(Message)
PrintMessage(Message)
End While
End Sub
Private Sub PrintMessage(ByVal Message As OutputMessage)
If Not Message.Color.HasValue Then
Message.RichTextBox.SelectionColor = Color.Black
Else
Message.RichTextBox.SelectionColor = Message.Color.Value
End If
Message.RichTextBox.SelectedText = Message.Text
If Message.ScrollToBottom Then
Message.RichTextBox.Select(Message.RichTextBox.Text.Length - 1, 0)
Message.RichTextBox.ScrollToCaret()
End If
Message.RichTextBox.AppendText(vbCrLf)
End Sub
Finally, in your threads:
Public Sub LogInBoxOutput2(ByVal [RichTextBox] As RichTextBox, textColor As Color?, ByVal text As String, Optional logToFile As Boolean = False)
Dim textToWrite As String = "[" & Now.ToString("dd:MM:yyyy HH:mm:ss.fff") & "] - " & text
If logNodeValues Then
MessageQueue.Enqueue(New OutputMessage(textToWrite, textColor, scrollToBottom, [RichTextBox]))
End If
If logToFile Then
writeToFileQueue2.Enqueue(textToWrite & vbCrLf)
End If
End Sub
Related
I tried to receive data from medical 'Mindray bs 200' device through serial port. data received but is unreadable. Unable to find the kind of data encryption.
Here is the code that receives the data
Private Sub comPort_DataReceived(ByVal sender As Object, ByVal e As System.IO.Ports.SerialDataReceivedEventArgs) Handles comPort.DataReceived
Dim str As String = ""
If e.EventType = SerialData.Chars Then
Do
Dim bytecount As Integer = comPort.BytesToRead
If bytecount = 0 Then
Exit Do
End If
Dim byteBuffer(bytecount) As Byte
comPort.Encoding = Encoding.GetEncoding(28591)
' comPort.Encoding = Encoding.GetEncoding(1252)
'comPort.Encoding = Encoding.GetEncoding("Windows-1252")
comPort.Read(byteBuffer, 0, bytecount)
str = str & System.Text.Encoding.ASCII.GetString(byteBuffer, 0, 1)
' The str looks like
Loop
End If
RaiseEvent ScanDataRecieved(str)
End Sub
Here is the data received
??????????????????????????????????????????????????????????????????????????????????????????????????????????????????X???????????????????
While not for this particular device, this document discusses a device that uses the same RS232 communication protocol and recommends the following SerialPort settings:
Baud rate: 115200
DataBits: 8
StopBits: 1
Parity: None
No flow control
Note: Minimum baud rate is 57600.
According to this document
2.1 Message Grammar:
Each HL7 message is composed of segments that end with <CR>
3 Communication Process and Message Example:
A message of HL7 protocol is the format of: <SB> ddddd <EB><CR>
ASCII value (HEX)
<SB> (start of message): 0B (VT - vertical tab)
<EB> (end of message): 1C (FS - file separator)
<CR> (carriage return) 0D (carriage return)
In the code below, I'll show how to use a buffer to collect data when "start of message" is received (HEX: 0B) and fill the buffer until "end of message" (HEX: 1C) followed by a carriage return (HEX: 0D) is received - at which point we'll raise an event and/or output the data.
For testing, we'll also write the data to a file in the Documents folder - this file is deleted each time the program starts so that only data from the current execution of the program is contained in the file. Each byte of data is output as a 2-digit hexadecimal value which allows us to see if a value is a control character.
The following may be helpful for interpreting the values:
ASCII Table
ASCII Code - The extended ASCII table
Create a class (name: HelperSerialPort.vb)
HelperSerialPort.vb
Note: The code in method Port_DataReceived (within the if-elseif statements) is untested - I don't have the particular device that you're using to be able to test it.
'(.NET Framework) - Add reference: Project => Add Reference => Assemblies => System.Management
'add using statement: Using System.Management;
'
'For >= .NET 5, install NuGet Package: System.IO.Ports and System.Management
'add Imports statements: Imports System.IO.Ports; Imports System.Management;
'
'
Imports System.Management
Imports System.IO.Ports
'specify valid baud rates
Public Enum PortBaudRate As Integer
Baud57600 = 57600
Baud76800 = 76800
Baud115200 = 115200
End Enum
Public Class HelperSerialPort
Implements IDisposable
Private Const BufferSize As Integer = 4096 'this value may need to be changed
Private Port As SerialPort = Nothing
Private BytesReadMessage As Integer = 0 'used to hold message bytes read
Private Buffer(BufferSize) As Byte 'used to hold data
Private Filename As String = Nothing
Private IsMessage As Boolean = False
'events that can be subscribed to
Public Event DataReceived(ByVal sender As Object, ByVal data As String)
Public Event ErrorReceived(ByVal sender As Object, ByVal errMsg As String)
Sub New()
'set value
'data will be written to this file for testing
Filename = System.IO.Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), "Mindray BS200.txt")
If Not String.IsNullOrEmpty(Filename) AndAlso System.IO.File.Exists(Filename) Then
'delete existing file
System.IO.File.Delete(Filename)
End If
System.Diagnostics.Debug.WriteLine("Filename: '" & Filename & "'")
End Sub
Public Function Connect(ByVal comPort As String, ByVal Optional baudRate As PortBaudRate = PortBaudRate.Baud115200) As String
Dim errMsg As String = String.Empty
Dim portName As String = String.Empty
Dim result As String = String.Empty
If String.IsNullOrEmpty(comPort) Then
errMsg = "COM port not selected"
Throw New Exception(errMsg)
End If
Try
If Port Is Nothing Then
'create new instance
Port = New SerialPort(comPort)
'subscribe to events (add event handlers)
AddHandler Port.DataReceived, AddressOf Port_DataReceived
AddHandler Port.ErrorReceived, AddressOf Port_ErrorReceived
End If
If Not Port.IsOpen Then
'set properties
Port.BaudRate = baudRate
Port.Handshake = Handshake.None
'if parity is even or odd, then set DataBits = 7
'if parity is none, set DataBits = 8
Port.Parity = Parity.None
Port.DataBits = 8
Port.StopBits = StopBits.One
'Port.ReadTimeout = 500 'this value may need to be adjusted
Port.WriteTimeout = 500 'this value may need to be adjusted
Port.DtrEnable = True 'enable Data Terminal Ready
'Port.RtsEnable = True 'enable Request to Send
'Port.DiscardNull = True
'Port.ReceivedBytesThreshold = 1 'number of bytes that causes 'DataReceived' event to be raised; default is 1
'open port
Port.Open()
result = "Status: Connected"
Else
result = "Status: Already Connected"
End If
Catch ex As System.IO.IOException
errMsg = "Error: " & ex.Message
result = errMsg 'set value
Debug.WriteLine(errMsg)
Dispose()
Catch ex As Exception
errMsg = "Error: " & ex.Message
result = errMsg 'set value
Debug.WriteLine(errMsg)
Throw ex
End Try
Debug.WriteLine(result)
Return result
End Function
Public Function Disconnect() As String
Dispose()
Return "Status: Disconnected"
End Function
Public Sub Dispose() Implements System.IDisposable.Dispose
If Port IsNot Nothing Then
'unsubscribe from events (remove event handlers)
RemoveHandler Port.DataReceived, AddressOf Port_DataReceived
RemoveHandler Port.ErrorReceived, AddressOf Port_ErrorReceived
Port.Dispose()
Port = Nothing
Else
Debug.WriteLine("Info: Port is null")
End If
End Sub
Public Function IsPortOpen() As Boolean
If Port IsNot Nothing Then
If Port.IsOpen Then
Return True
Else
Try
Port.Dispose()
Catch ex As Exception
'do nothing
Debug.WriteLine("Error (IsPortOpen): " & ex.Message)
End Try
End If
Port = Nothing
System.GC.Collect()
System.GC.Collect()
End If
Return False
End Function
Private Sub Port_DataReceived(ByVal sender As Object, ByVal e As SerialDataReceivedEventArgs)
'ToDo: add desired code
Dim bytesRead As Integer = 0
Dim bytesToRead As Integer = 1
Dim errMsg As String = String.Empty
'read SerialPort data
Do
'read 1 byte at a time
bytesRead = Port.Read(Buffer, BytesReadMessage, bytesToRead)
'when <SB> (HEX: 0B) is received, remove all existing data from buffer
'this will result in the buffer only containing our desired data.
'when <EB> (HEX: 1C) is received followed by <CR> (HEX: 0D),
'this is the end of the message, so we'll send the message and/or display it.
'Buffer(BytesReadMessage) contains the last byte of data that was read
'
If Buffer(BytesReadMessage) = &HB Then
'0x0B - VT (vertical tab); in the documentation this is '<SB>' - start of message
System.Diagnostics.Debug.WriteLine("<SB> (start of message)")
'remove existing data (get ready for new message)
Array.Clear(Buffer, 0, Buffer.Length)
're-initialize to -1. It's incremented below which will result in the value being 0
BytesReadMessage = -1
'set value - start saving data to the buffer
IsMessage = True
ElseIf IsMessage AndAlso Buffer(BytesReadMessage) = &HD AndAlso Buffer(BytesReadMessage - 1) = &H1C Then
'0x1C - FS (file separator); in the documentation this is '<EB>' - end of message
'0x0D - CR (carriage return); in the documentation this is '<CR>'
System.Diagnostics.Debug.WriteLine("<EB> (end of message)")
'raise event to send data
SendData(Buffer)
'set value - stop saving data to the buffer
IsMessage = False
ElseIf Not IsMessage Then
'for debugging/testing, display non-message data
System.Diagnostics.Debug.WriteLine("Non-message data: " & Buffer(BytesReadMessage).ToString("X2"))
End If
'set value
BytesReadMessage += bytesRead
Debug.WriteLine("Info: BytesReadMessage: " & BytesReadMessage.ToString() & " bytesRead: " & bytesRead.ToString() & " Port.BytesToRead: " & Port.BytesToRead.ToString())
Loop While (Port.BytesToRead > 0)
Debug.WriteLine(String.Format("{0}---------------------------{0}", System.Environment.NewLine))
End Sub
Private Sub Port_ErrorReceived(ByVal sender As Object, ByVal e As SerialErrorReceivedEventArgs)
'ToDo: add desired code
Dim errMsg As String = e.EventType.ToString()
Debug.WriteLine("Port_ErrorReceived: " & errMsg)
'raise event
RaiseEvent ErrorReceived(Me, errMsg)
End Sub
Public Sub SendData(buffer() As Byte)
'ToDo: modify the code below and/or add desired code
'convert to a string that's human-readable
Dim data As String = String.Empty
If buffer IsNot Nothing Then
For i As Integer = 0 To BytesReadMessage Step 1
If Not String.IsNullOrEmpty(data) Then
data += " " 'append space
End If
'for testing, convert to a 2-digit HEX value
data += buffer(i).ToString("X2")
Next
'add newline
data += System.Environment.NewLine
End If
System.Diagnostics.Debug.WriteLine("data: " & data)
'for testing save to file
If Not String.IsNullOrEmpty(Filename) Then
'append data to file
System.IO.File.AppendAllText(Filename, data)
End If
'raise event
RaiseEvent DataReceived(Me, data)
'Note: "When receiving the message, the LIS (Laboratory Information Management System) host first judges the legality and type of the message and then replies accordingly."
'ToDo: A response needs to be sent for each message received (see documentation for information on how to create the appropriate message - "3 Communication Process and Message Example")
End Sub
Public Sub WriteToSerialPort(ByVal data As String)
Dim errMsg As String = String.Empty
Try
If Port.IsOpen Then
'convert string to Byte array
Dim hexArr As Byte() = System.Text.Encoding.ASCII.GetBytes(data)
For Each hexVal As Byte In hexArr
'convert byte to byte array
Dim tempArr As Byte() = New Byte() {hexVal}
'write
Port.Write(tempArr, 0, 1)
'add 1 ms delay before writing next byte
System.Threading.Thread.Sleep(1)
Next
Else
errMsg = "Error: Port is not open. Please open the connection and try again."
Debug.WriteLine(errMsg)
Throw New Exception(errMsg)
End If
Catch ex As Exception
errMsg = "Error: " & ex.Message
Debug.WriteLine(errMsg)
Throw ex
End Try
End Sub
End Class
Below is an alternative version of method Port_DataReceived that uses ReadExisting() instead - however, it may not work in your situation.
Port_DataReceived (version that uses ReadExisting):
Private Sub Port_DataReceived(ByVal sender As Object, ByVal e As SerialDataReceivedEventArgs)
'ToDo: add desired code
'read SerialPort data
Dim data As String = String.Empty
data = Port.ReadExisting()
'data = Port.ReadLine
Debug.WriteLine("Port_DataReceived: " & data)
'raise event
RaiseEvent DataReceived(Me, data)
End Sub
Form1:
Add a Button (name: btnConnectDisconnect)
Double-click the button to add the event handler
Add a RichTextBox (name: RichTextBox1)
Form1.vb
Note: In the code below, you'll need to update helper.Connect("COM1") with the correct COM port.
Public Class Form1
Private helper As HelperSerialPort = New HelperSerialPort()
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
End Sub
Private Sub Disconnect()
If helper IsNot Nothing Then
'unsubscribe from event(s)
RemoveHandler helper.DataReceived, AddressOf Helper_DataReceived
RemoveHandler helper.ErrorReceived, AddressOf Helper_ErrorReceived
helper.Dispose()
helper = Nothing
End If
End Sub
Private Sub btnConnectDisconnect_Click(sender As Object, e As EventArgs) Handles btnConnectDisconnect.Click
If helper Is Nothing Then
'create new instance
helper = New HelperSerialPort()
End If
If btnConnectDisconnect.Text = "Connect" Then
'clear existing data
RichTextBox1.Clear()
'ToDo: change to your desired COM port
helper.Connect("COM1")
'subscribe to event(s)
AddHandler helper.DataReceived, AddressOf Helper_DataReceived
AddHandler helper.ErrorReceived, AddressOf Helper_ErrorReceived
'set text
btnConnectDisconnect.Text = "Disconnect"
btnConnectDisconnect.Refresh()
Else
Disconnect()
'set text
btnConnectDisconnect.Text = "Connect"
btnConnectDisconnect.Refresh()
End If
End Sub
Private Sub Helper_DataReceived(ByVal sender As Object, ByVal data As String)
'ToDo: add desired code
System.Diagnostics.Debug.WriteLine(DateTime.Now.ToString("HH:mm:ss") & " - Helper_DataReceived: " & data)
'append data to RichTextBox
RichTextBox1.Invoke(New MethodInvoker(Sub()
RichTextBox1.AppendText(DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss.fff") & " - " & data)
End Sub))
End Sub
Private Sub Helper_ErrorReceived(ByVal sender As Object, ByVal errMsg As String)
'ToDo: add desired code
System.Diagnostics.Debug.WriteLine(DateTime.Now.ToString("HH:mm:ss") & " - Helper_ErrorReceived: " & errMsg)
'append error message to RichTextBox
RichTextBox1.Invoke(New MethodInvoker(Sub()
RichTextBox1.AppendText(errMsg)
End Sub))
End Sub
Private Sub Form1_FormClosed(sender As Object, e As FormClosedEventArgs) Handles MyBase.FormClosed
Disconnect()
End Sub
End Class
Resources:
Host Interface Manual
HL7 Version 2.3.1
Modem control signals
This question already has answers here:
How to create a popup menu in visual basic.net?
(2 answers)
Closed 5 years ago.
I have this code in VBA (shown in a simplified version) :
Sub TheMenu()
Dim Obj As CommandBar
Set Obj = Application.CommandBars.Add(Position:=msoBarPopup, MenuBar:=False, Temporary:=True)
Obj.Controls.Add(Type:=msoControlButton).Caption = "Button1"
Obj.Controls.Add(Type:=msoControlButton).Caption = "Button2"
Obj.ShowPopup
End Sub
I wish to make something equivalent (meaning "that looks similar and has similar uses", I don't need more) in VB.NET. Do you know any way of doing so?
I am using in VS2015, a "Windows Forms application" project using .NET framework 4.6.1 .
You can follow the example bellow here:
Public Class Connect
Implements Extensibility.IDTExtensibility2
Implements IDTCommandTarget
Private Const MY_COMMAND_NAME As String = "MyCommand"
Private applicationObject As EnvDTE.DTE
Private addInInstance As EnvDTE.AddIn
Private myStandardCommandBarControl As CommandBarControl
Private myToolsCommandBarControl As CommandBarControl
Private myCodeWindowCommandBarControl As CommandBarControl
Private myTemporaryToolbar As CommandBar
Private myTemporaryCommandBarPopup As CommandBarPopup
Public Sub OnConnection(ByVal application As Object, ByVal connectMode _
As Extensibility.ext_ConnectMode, ByVal addInInst As Object, _
ByRef custom As System.Array) Implements Extensibility.IDTExtensibility2.OnConnection
Dim myCommand As Command
Dim standardCommandBar As CommandBar
Dim menuCommandBar As CommandBar
Dim toolsCommandBar As CommandBar
Dim codeCommandBar As CommandBar
Dim toolsCommandBarControl As CommandBarControl
Dim myCommandBarButton As CommandBarButton
Dim position As Integer
Try
applicationObject = CType(application, EnvDTE.DTE)
addInInstance = CType(addInInst, EnvDTE.AddIn)
Select Case connectMode
Case ext_ConnectMode.ext_cm_AfterStartup, ext_ConnectMode.ext_cm_Startup
' Try to retrieve the command, just in case it was already created
Try
myCommand = applicationObject.Commands.Item(addInInstance.ProgID & "." & "MyCommand")
Catch
End Try
' Add the command if it does not exists
If myCommand Is Nothing Then
myCommand = applicationObject.Commands.AddNamedCommand(addInInstance, _
"MyCommand", "MyCommand", "Executes the command for MyAddin", True, 59, Nothing, _
vsCommandStatus.vsCommandStatusSupported Or vsCommandStatus.vsCommandStatusEnabled)
End If
' Retrieve some built-in command bars
standardCommandBar = applicationObject.CommandBars.Item("Standard")
menuCommandBar = applicationObject.CommandBars.Item("MenuBar")
toolsCommandBar = applicationObject.CommandBars.Item("Tools")
codeCommandBar = applicationObject.CommandBars.Item("Code Window")
' Add a button to the built-in "Standard" toolbar
myStandardCommandBarControl = myCommand.AddControl(standardCommandBar, _
standardCommandBar.Controls.Count + 1)
myStandardCommandBarControl.Caption = MY_COMMAND_NAME
' Change the button style, which must be done casting the control to a button
myCommandBarButton = DirectCast(myStandardCommandBarControl, CommandBarButton)
myCommandBarButton.Style = MsoButtonStyle.msoButtonIcon
' Add a button to the built-in "Tools" menu
myToolsCommandBarControl = myCommand.AddControl(toolsCommandBar, toolsCommandBar.Controls.Count + 1)
myToolsCommandBarControl.Caption = MY_COMMAND_NAME
' Add a button to the built-in "Code Window" context menu
myCodeWindowCommandBarControl = myCommand.AddControl(codeCommandBar, codeCommandBar.Controls.Count + 1)
myCodeWindowCommandBarControl.Caption = MY_COMMAND_NAME
' Add a new toolbar with a button on it
myTemporaryToolbar = applicationObject.CommandBars.Add("MyTemporaryToolbar", _
MsoBarPosition.msoBarTop, System.Type.Missing, True)
' Change the button style, which must be done casting the control to a button
myCommandBarButton = DirectCast(myCommand.AddControl(myTemporaryToolbar), CommandBarButton)
myCommandBarButton.Style = MsoButtonStyle.msoButtonIcon
' Make visible the toolbar
myTemporaryToolbar.Visible = True
' Calculate the position of a new command bar popup by the "Tools" menu
toolsCommandBarControl = DirectCast(toolsCommandBar.Parent, CommandBarControl)
position = toolsCommandBarControl.Index + 1
' Add a new command bar popup with a button on it
myTemporaryCommandBarPopup = DirectCast(menuCommandBar.Controls.Add( _
MsoControlType.msoControlPopup, System.Type.Missing, System.Type.Missing, _
position, True), CommandBarPopup)
myTemporaryCommandBarPopup.CommandBar.Name = "MyTemporaryCommandBarPopup"
myTemporaryCommandBarPopup.Caption = "My menu"
myCommand.AddControl(myTemporaryCommandBarPopup.CommandBar)
myTemporaryCommandBarPopup.Visible = True
End Select
Catch e As System.Exception
System.Windows.Forms.MessageBox.Show(e.ToString)
End Try
End Sub
Public Sub OnDisconnection(ByVal RemoveMode As Extensibility.ext_DisconnectMode, _
ByRef custom As System.Array) Implements Extensibility.IDTExtensibility2.OnDisconnection
Try
If Not (myStandardCommandBarControl Is Nothing) Then
myStandardCommandBarControl.Delete()
End If
If Not (myCodeWindowCommandBarControl Is Nothing) Then
myCodeWindowCommandBarControl.Delete()
End If
If Not (myToolsCommandBarControl Is Nothing) Then
myToolsCommandBarControl.Delete()
End If
If Not (myTemporaryToolbar Is Nothing) Then
myTemporaryToolbar.Delete()
End If
If Not (myTemporaryCommandBarPopup Is Nothing) Then
myTemporaryCommandBarPopup.Delete()
End If
Catch e As System.Exception
System.Windows.Forms.MessageBox.Show(e.ToString)
End Try
End Sub
Public Sub OnBeginShutdown(ByRef custom As System.Array) Implements Extensibility.IDTExtensibility2.OnBeginShutdown
End Sub
Public Sub OnAddInsUpdate(ByRef custom As System.Array) Implements Extensibility.IDTExtensibility2.OnAddInsUpdate
End Sub
Public Sub OnStartupComplete(ByRef custom As System.Array) Implements _
Extensibility.IDTExtensibility2.OnStartupComplete
End Sub
Public Sub Exec(ByVal cmdName As String, ByVal executeOption As vsCommandExecOption, _
ByRef varIn As Object, ByRef varOut As Object, ByRef handled As Boolean) Implements IDTCommandTarget.Exec
handled = False
If (executeOption = vsCommandExecOption.vsCommandExecOptionDoDefault) Then
If cmdName = addInInstance.ProgID & "." & MY_COMMAND_NAME Then
handled = True
System.Windows.Forms.MessageBox.Show("Command executed.")
End If
End If
End Sub
Public Sub QueryStatus(ByVal cmdName As String, ByVal neededText As vsCommandStatusTextWanted, _
ByRef statusOption As vsCommandStatus, ByRef commandText As Object) Implements IDTCommandTarget.QueryStatus
If neededText = EnvDTE.vsCommandStatusTextWanted.vsCommandStatusTextWantedNone Then
If cmdName = addInInstance.ProgID & "." & MY_COMMAND_NAME Then
statusOption = CType(vsCommandStatus.vsCommandStatusEnabled + _
vsCommandStatus.vsCommandStatusSupported, vsCommandStatus)
Else
statusOption = vsCommandStatus.vsCommandStatusUnsupported
End If
End If
End Sub
End Class
So my problem is:
I have a List of a custom Type {Id as Integer, Tag() as String},
and i want to perform a multiple-criteria search on it; eg:
SearchTags={"Document","HelloWorld"}
Results of the Search will be placed a ListBox (ListBox1) in this format:
resultItem.id & " - " & resultItem.tags
I already tried everything i could find on forums, but it didn't work for me (It was for db's or for string datatypes)
Now, i really need your help. Thanks in advance.
For Each MEntry As EntryType In MainList
For Each Entry In MEntry.getTags
For Each item As String In Split(TextBox1.Text, " ")
If Entry.Contains(item) Then
If TestIfItemExistsInListBox2(item) = False Then
ListBox1.Items.Add(item & " - " & Entry.getId)
End If
End If
Next
Next
Next
Example Custom Array:
(24,{"snippet","vb"})
(32,{"console","cpp","helloworld"})
and so on...
I searched for ("Snippet vb test"):
snippet vb helloWorld - 2
snippet vb tcpchatEx - 16
cs something
test
So, i'll get everything that contains one of my search phrases.
I expected following:
snippet vb tcp test
snippet vb dll test
snippet vb test metroui
So, i want to get everything that contains all my search phrases.
My entire, code-likely class
Imports Newtonsoft.Json
Public Class Form2
Private Sub Form2_Load(sender As Object, e As EventArgs) Handles MyBase.Load
End Sub
Dim MainList As New List(Of EntryType)
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
MainList.Clear()
Dim thr As New Threading.Thread(AddressOf thr1)
thr.SetApartmentState(Threading.ApartmentState.MTA)
thr.Start()
End Sub
Delegate Sub SetTextCallback([text] As String)
Private Sub SetTitle(ByVal [text] As String) ' source <> mine
If Me.TextBox1.InvokeRequired Then
Dim d As New SetTextCallback(AddressOf SetTitle)
Me.Invoke(d, New Object() {[text]})
Else
Me.Text = [text]
End If
End Sub
Sub thr1()
Dim linez As Integer = 1
Dim linex As Integer = 1
For Each line As String In System.IO.File.ReadAllLines("index.db")
linez += 1
Next
For Each line As String In System.IO.File.ReadAllLines("index.db")
Try
Application.DoEvents()
Dim a As saLoginResponse = JsonConvert.DeserializeObject(Of saLoginResponse)(line) ' source <> mine
Application.DoEvents()
MainList.Add(New EntryType(a.id, Split(a.tags, " ")))
linex += 1
SetTitle("Search (loading, " & linex & " of " & linez & ")")
Catch ex As Exception
End Try
Next
SetTitle("Search")
End Sub
Private Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click
Dim searchTags() As String = TextBox1.Text.Split(" ")
Dim query = MainList.Where(Function(et) et.Tags.Any(Function(tag) searchTags.Contains(tag))).ToList
For Each et In query
ListBox1.Items.Add(et.Id)
Next
End Sub
Private Sub Button4_Click(sender As Object, e As EventArgs) ' test
MsgBox(Mid(ListBox1.SelectedItem.ToString, 1, 6)) ' test
End Sub 'test, removeonrelease
End Class
Public Class EntryType
Public Property Id As Integer
Public Property Tags() As String
Public Sub New(ByVal _id As Integer, ByVal _tags() As String)
Me.Id = Id
Me.Tags = Tags
End Sub
Public Function GetTags() As String
'to tell the Listbox what to display
Return Tags
End Function
Public Function GetId() As Integer
'to tell the Listbox what to display
Return Id
End Function
End Class
I also edited your EntryType class; I added a constructor, removed toString and added GetTags and GetID.
Example "DB" im working with ("db" as "index.db" in exec dir):
{"tags":"vb.net lol test qwikscopeZ","id":123456}
{"tags":"vb.net lol test","id":12345}
{"tags":"vb.net lol","id":1234}
{"tags":"vb.net","id":123}
{"tags":"cpp","id":1}
{"tags":"cpp graphical","id":2}
{"tags":"cpp graphical fractals","id":3}
{"tags":"cpp graphical fractals m4th","id":500123}
Error:
Debugger:Exception Intercepted: _Lambda$__1, Form2.vb line 44
An exception was intercepted and the call stack unwound to the point before the call from user code where the exception occurred. "Unwind the call stack on unhandled exceptions" is selected in the debugger options.
Time: 13.11.2014 03:46:10
Thread:<No Name>[5856]
Here is a Lambda query. The Where filters on a predicate, since Tags is an Array you can use the Any function to perform a search based on another Array-SearchTags. You can store each class object in the Listbox since it stores Objects, you just need to tell it what to display(see below).
Public Class EntryType
Public Property Id As Integer
Public Property Tags() As As String
Public Overrides Function ToString() As String
'to tell the Listbox what to display
Return String.Format("{0} - {1}", Me.Id, String.Join(Me.Tags, " "))
End Function
End Class
Dim searchTags = textbox1.Text.Split(" "c)
Dim query = mainlist.Where(Function(et) et.Tags.Any(Function(tag) searchTags.Contains(tag))).ToList
For Each et In query
Listbox1.Items.Add(et)
Next
I am using vb.net to download a file and using Tamir.SharpSsh which works well except its slow it takes about 50 seconds to download a 3.5 kb file. My Question is How would I best put a wait function in for 2 mins to ensure the file is downloaded.
Public Function DownloadPricat() As Boolean
Dim retVal As Boolean
Dim PRICAT_CSV As String
Dim sfilename As String = ""
Dim ifilename As String
utils = New ThreeSoftware.Configuration.Utilities.utilConfigurationLoader("CONFIGURATION FILES\GEMINI RELATED\SkechersImport.ini")
Hostname = utils.GetIniSetting("SSH SECTION", "SSH_HOST", "")
username = utils.GetIniSetting("SSH SECTION", "SSH_USERNAME", "")
passsword = utils.GetIniSetting("SSH SECTION", "SSH_PASSWORD", "")
port = utils.GetIniSetting("SSH SECTION", "SSH_PORT", "")
HomeDirectoy = utils.GetIniSetting("SSH SECTION", "SSH_REMOTE_DIRECTORY", "")
transfer = New wcSFtp(Hostname, Integer.Parse(port), username, passsword)
PRICAT_CSV = utils.GetIniSetting("PATHS SECTION", "PRICAT_CSV", "")
sfilename = utils.GetIniSetting("PATHS SECTION", "PRICAT_FILENAME", "")
ifilename = PRICAT_CSV & "\" & sfilename
If transfer.getFile(HomeDirectoy & "Pricat.edi", ifilename) = True Then
MsgBox("Download Complete", vbInformation, "Import")
retVal = True
Else
retVal = False
End If
End Function
Get File is simply this
Public Function getFile(ByVal remotePath As String, ByVal localFile As String) As Boolean
Try
transfer = New Sftp(Me._hostname, Me._username, Me._password)
transfer.Connect(Me._port)
transfer.Get(remotePath, localFile)
transfer.Close()
Return True
Catch ex As Exception
Debug.Print("Error downloading file: " & ex.ToString)
Return False
End Try
End Function
Put the whole download into a backgroundworker's DoWork() function and add the Boolean result as the result-variable of the eventargs variable.
Then handle the RunWorkerCompleted() event of the backgroundworker and perform whatever task you want to happen after the download from there. That way you make sure the download is actually finished.
Public Class Form1
Private WithEvents LazyBGW As New System.ComponentModel.BackgroundWorker
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
'Initiate the backgroundworker. It runs in another thread.
LazyBGW.RunWorkerAsync()
End Sub
Private Sub LazyBGW_DoWork(sender As Object, e As System.ComponentModel.DoWorkEventArgs) Handles LazyBGW.DoWork
'This code runs in the BGW-Thread
'Perform the whole download task here or just call your
e.Result = DownloadPricat()
'Work is done, put results in the eventargs-variable for further processing
End Sub
Private Sub LazyBGW_RunWorkerCompleted(sender As Object, e As System.ComponentModel.RunWorkerCompletedEventArgs) Handles LazyBGW.RunWorkerCompleted
'This code runs in the UI-Thread
Dim results As Boolean = CBool(e.Result)
MessageBox.Show("The worker is done and our result is: " & results.ToString)
End Sub
End Class
Edit:
In a console app you can use a Task:
Module Module1
Private Function DownloadPricat() As Boolean
Threading.Thread.Sleep(10000)
Return True
End Function
Sub Main()
Dim DLTask As New System.Threading.Tasks.Task(Of Boolean)(Function() DownloadPricat())
DLTask.Start()
Dim ThisTime As Date = Date.Now
Console.Write("Downloading")
While DLTask.IsCompleted = False AndAlso DLTask.IsCanceled = False AndAlso DLTask.IsFaulted = False
If (Date.Now - ThisTime).TotalSeconds > 1 Then
Console.Write(".")
ThisTime = Date.Now
End If
End While
Console.Write("Done.")
End Sub
End Module
I'm having an issue where my main form isn't updating even though I see the event fire off. Let me explain the situation and share some of my code which I'm sure will be horrible since I'm an amateur.
I created a class to take in the settings for running a process in the background. I add some custom events in that class so I could use that in my form instead of a timer.
I put a break on the two subs for that handle those events and I see them get kicked off as soon as an install starts.
I look at the data and it's coming across and no exceptions are thrown.
At first I thought it was because the datagridview had some latency issues. I set that to be double buffered through some tricks I found but it didn't matter. There was still a roughly 10 second delay before the data showed up in the datagrid.
I thought about it and decided I really didn't need a datagridview and replaced the control with a multiline textbox, but it didn't make a difference. It's still taking 10 seconds or longer to show updates to the form/textbox.
I've included some of my code below.
Public Shared WithEvents np As NewProcess
Private Sub Form1_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
Try
np = New NewProcess
AddHandler np.InstallFinished, AddressOf np_InstallFinished
AddHandler np.InstallStarted, AddressOf np_InstallStarted
Catch ex As Exception
End Try
End Sub
Protected Sub np_InstallFinished(ByVal Description As String, ByVal ExitCode As Integer)
InstallInProcess = False
If Not Description = Nothing Then
If Not ExitCode = Nothing Then
AddLog(String.Format("Completed install of {0} ({1}).", Description, ExitCode))
Else
AddLog(String.Format("Completed install of {0}.", Description))
End If
End If
RefreshButtons()
UpdateListofApps()
np.Dispose()
End Sub
Protected Sub np_InstallStarted(ByVal Description As String)
InstallInProcess = True
If Not Description = Nothing Then AddLog(String.Format("Started the install of {0}.", Description))
End Sub
Public Class NewProcess
Dim ProcessName As String
Dim ProcessVisibile As Boolean
Dim Arguments As String
Dim WaitforExit As Boolean
Dim Description As String
Dim ShellExecute As Boolean
Dim EC As Integer = Nothing 'Exit Code
Private IsBusy As Boolean = Nothing
Dim th As Threading.Thread
Public Event InstallFinished(ByVal Description As String, ByVal ExitCode As Integer)
Public Event InstallStarted(ByVal Description As String)
Public Function Busy() As Boolean
If IsBusy = Nothing Then Return False
Return IsBusy
End Function
Public Function ExitCode() As Integer
Return EC
End Function
Public Function ProcessDescription() As String
Return Description
End Function
''' <summary>
''' Starts a new multithreaded process.
''' </summary>
''' <param name="path">Path of the File to run</param>
''' <param name="Visible">Should application be visible?</param>
''' <param name="Arg">Arguments</param>
''' <param name="WaitforExit">Wait for application to exit?</param>
''' <param name="Description">Description that will show up in logs</param>
''' <remarks>Starts a new multithreaded process.</remarks>
Public Sub StartProcess(ByVal path As String, ByVal Visible As Boolean, Optional ByVal Arg As String = Nothing, Optional ByVal WaitforExit As Boolean = False, Optional ByVal Description As String = Nothing)
Try
Me.ProcessName = path
Me.ProcessVisibile = Visible
If Arguments = Nothing Then Me.Arguments = Arg
Me.Description = Description
Me.WaitforExit = WaitforExit
If IsBusy And WaitforExit Then
MessageBox.Show("Another install is already in process, please wait for previous install to finish.", "Warning", MessageBoxButtons.OK, MessageBoxIcon.Warning)
Exit Sub
End If
If Not fn_FileExists(ProcessName) Then
MessageBox.Show("Could not find file " & ProcessName & ".", "Could not start process because file is missing.", MessageBoxButtons.OK, MessageBoxIcon.Error)
Exit Sub
End If
th = New Threading.Thread(AddressOf NewThread)
With th
.IsBackground = True
If Not Description Is Nothing Then .Name = Description
.Start()
End With
Catch ex As Exception
End Try
End Sub
Private Sub NewThread()
Dim p As Process
Try
p = New Process
With p
.EnableRaisingEvents = True
.StartInfo.Arguments = Arguments
.StartInfo.FileName = ProcessName
.StartInfo.CreateNoWindow = ProcessVisibile
End With
If ProcessVisibile Then
p.StartInfo.WindowStyle = ProcessWindowStyle.Normal
Else
p.StartInfo.WindowStyle = ProcessWindowStyle.Hidden
End If
p.Start()
IsBusy = True
RaiseEvent InstallStarted(Description)
If WaitforExit Then
Do While p.HasExited = False
Threading.Thread.Sleep(500)
Loop
IsBusy = False
RaiseEvent InstallFinished(Description, p.ExitCode)
End If
EC = p.ExitCode
Catch ex As Exception
End Try
End Sub
Public Sub Dispose()
ProcessName = Nothing
ProcessVisibile = Nothing
Arguments = Nothing
WaitforExit = Nothing
Description = Nothing
EC = Nothing
InstallInProcess = Nothing
th.Join()
MemoryManagement.FlushMemory()
End Sub
End Class
Sub AddLog(ByVal s As String)
Try
s = String.Format("[{0}] {1}", TimeOfDay.ToShortTimeString, s)
Form1.tbLogs.AppendText(s & vbCrLf)
Using st As New StreamWriter(LogFilePath, True)
st.WriteLine(s)
st.Flush()
End Using
Catch ex As Exception
End Try
End Sub
Any idea's? I'm at a complete loss.
I've tried adding application.doevents, me.refresh and quite a few other things :(
Form1.tbLogs.AppendText(s & vbCrLf)
Standard VB.NET trap. Form1 is a class name, not a reference to the form. Unfortunately, VB.NET implemented an anachronism from VB6 where that was legal. It however falls apart when you use threads. You'll get another form object automatically created, one that isn't visible because its Show() method was never called. Otherwise dead as a doornail since the thread is not pumping a message loop.
You'll need to pass a reference to the actual form object that the user is looking at to the worker class. The value of Me in the Form1 code. You will also have to use Control.Invoke since it isn't legal to update controls from another thread. I recommend you fire an event instead, one that Form1 can subscribe to, so that your worker class isn't infected with implementation details of the UI.
Some suggestions:
First make it work without threads. (Ironical you call yourself an amateur, It so happened I only learned to do that after I was past the 'amateur' stage)
Don't try to update the GUI Controls from a background thread. That's forbidden in windows. I'm not sure that's what you do (no VB guru), but it sure looks like it.
Use the .net BackgroundWorker class. It has builtin functionality to talk back to the main thread from a background thread. It's not perfect but a good start.
You got me pointed in the right direction. Thanks Hans. This was my solution:
Private Sub SetText(ByVal [text] As String)
If Me.tbLogs.InvokeRequired Then
Dim d As New SetTextCallback(AddressOf SetText)
Me.Invoke(d, New Object() {[text]})
Else
Me.tbLogs.Text = [text]
End If
End Sub
Private Sub np_InstallStarted(ByVal Description As String)
InstallInProcess = True
If Me.tbLogs.Text = "" Then
SetText(String.Format("[{0}] Started the install of {1}.{2}", TimeOfDay.ToShortTimeString, Description, vbCrLf))
Else
SetText(tbLogs.Text & vbCrLf & String.Format("[{0}] Started the install of {1}.{2}", TimeOfDay.ToShortTimeString, Description, vbCrLf))
End If
End Sub
Private Sub np_InstallFinished(ByVal [Description] As String, ByVal [ExitCode] As Integer)
InstallInProcess = False
If Not Description = Nothing Then
If Not ExitCode = Nothing Then
SetText(tbLogs.Text & vbCrLf & String.Format("[{0}] Completed install of {1} ({2}).{3}", TimeOfDay.ToShortTimeString, Description, ExitCode, vbCrLf))
Else
SetText(tbLogs.Text & vbCrLf & String.Format("[{0}] Completed install of {1}.{3}", TimeOfDay.ToShortTimeString, Description, vbCrLf))
End If
End If
RefreshButtons()
UpdateListofApps()
np.Dispose()
End Sub
So when the event kicks off that the install has started or finished, I use the SetText to update the log on the original form.
Problem is I posted that original post as an "unregistered user" so now I'm trying to figure out a way to say the question was answered. Thanks again for your help!