How do I get events to execute on the "main thread" - vb.net

I have 2 classes, one is frmMain a windows form and the other is a class in vb.NET 2003.
frmMain contains a start button which executes the monitor function in the other class. My question is, I am manually adding the event handlers -- when the events are executed how do I get them to excute on the "main thread". Because when the tooltip balloon pops up at the tray icon it displays a second tray icon instead of popping up at the existing tray icon. I can confirm that this is because the events are firing on new threads because if I try to display a balloon tooltip from frmMain it will display on the existing tray icon.
Here is a part of the second class (not the entire thing):
Friend Class monitorFolders
Private _watchFolder As System.IO.FileSystemWatcher = New System.IO.FileSystemWatcher
Private _eType As evtType
Private Enum evtType
changed
created
deleted
renamed
End Enum
Friend Sub monitor(ByVal path As String)
_watchFolder.Path = path
'Add a list of Filter to specify
_watchFolder.NotifyFilter = IO.NotifyFilters.DirectoryName
_watchFolder.NotifyFilter = _watchFolder.NotifyFilter Or IO.NotifyFilters.FileName
_watchFolder.NotifyFilter = _watchFolder.NotifyFilter Or IO.NotifyFilters.Attributes
'Add event handlers for each type of event that can occur
AddHandler _watchFolder.Changed, AddressOf change
AddHandler _watchFolder.Created, AddressOf change
AddHandler _watchFolder.Deleted, AddressOf change
AddHandler _watchFolder.Renamed, AddressOf Rename
'Start watching for events
_watchFolder.EnableRaisingEvents = True
End Sub
Private Sub change(ByVal source As Object, ByVal e As System.IO.FileSystemEventArgs)
If e.ChangeType = IO.WatcherChangeTypes.Changed Then
_eType = evtType.changed
notification()
End If
If e.ChangeType = IO.WatcherChangeTypes.Created Then
_eType = evtType.created
notification()
End If
If e.ChangeType = IO.WatcherChangeTypes.Deleted Then
_eType = evtType.deleted
notification()
End If
End Sub
Private Sub notification()
_mainForm.NotifyIcon1.ShowBalloonTip(500, "Folder Monitor", "A file has been " + [Enum].GetName(GetType(evtType), _eType), ToolTipIcon.Info)
End Sub
End Class

You need to use Control.Invoke, this will not run the delegate (event) on the UI thread, but when the event fires, you can use Control.Invoke to execute a piece of code on the UI thread, in your case this code will be a function that shows the tool tip.

Thanks, I figured it out by doing the following in frmMain and in the other class calling a new method in frmMain called dispalyToolTip.
In FrmMain here is what I did:
added a delegate
Private Delegate Sub displayTooltipDelegate(ByVal tooltipText As String)
Added the new method, which I am calling from the other class
Friend Sub displayTooltip(ByVal tooltipText As String)
If Me.InvokeRequired Then
Dim delegate1 As New displayTooltipDelegate(AddressOf displayTooltip)
Me.Invoke(delegate1, tooltipText)
Else
NotifyIcon1.ShowBalloonTip(500, "Folder Monitor", tooltipText, ToolTipIcon.Info)
End If
End Sub

Related

Accessing label on Passed Form

I'm trying to add an activity logger into my application. I'm hoping to keep my code clean and so only want to state the current activity once in my code and then it displays it in lblStatus and updates the log file. I'm trying to do this like this:
I'm passing it like this on my normal form:
LogActivity.LogActivity(Me, "Checking program directories...")
And this is the sub that's doing the work.
Public Sub LogActivity(FormID As Form, ByVal ActivityDescription As String)
'Log Activity code is here
'Update Status Label on form
FormID.lblStatus = ActivityDescription
end sub
But Visual Studio doesn't understand the syntax, I can understand why but I'm not sure how to do this correctly.
'lblStatus' is not a member of 'Form'
However, all my forms will call this sub, so I really need my code to understand which form called the sub and to update the lbl on that form specifically.
I could just check the form's name like this:
If Form.Name = "Main_Loader" Then
Main_Loader.lblStatus = ActivityDescription
elseif Form.Name = "..." then
end if
But again that's not very clean and doesn't seem like the proper way... Could anyone advise?
Assuming the Label is called "lblStatus" on ALL of the Forms, you could simply use Controls.Find() like this:
Public Sub LogActivity(FormID As Form, ByVal ActivityDescription As String)
Dim matches() As Control = FormID.Controls.Find("lblStatus", True)
If matches.Length > 0 AndAlso TypeOf matches(0) Is Label Then
Dim lbl As Label = DirectCast(matches(0), Label)
lbl.Text = ActivityDescription
End If
End Sub
You could use a custom event. The form that needs the info reported back subscribes to the event that is on the form that has the LogActivity.
Public Class frmActivity 'name of class is an example
Private log As New LogClass
Public Event LogActivity(ActivityDescription As String, log As LogClass)
'somewhere in code raise this event and send to info to main form
Private Sub SomeEventSub()
RaiseEvent LogActivity("some status", log)
End Sub
'...
End Class
Public Class frmMain 'name of class is an example
Private Sub btnGetActivity() Handles btnGetActivity.Click
Dim frm As New frmActivity
Addhandler frm.LogActivity, AddressOf LogActivity
frm.Show()
End SUb
Private Sub LogActivity(ActivityDescription As String, log As LogClass)
lblStatus = ActivityDescription
'then use the log variable to store the log data
End Sub
'...
End Class

Handling Event - form/control not updating?

First off, I do not work with winform development full time so don't bash me too bad...
As the title somewhat depicts, I am having an issue refreshing the controls on a form after an event has been raised and captured.
On "Form1" I have a Dockpanel and am creating two new forms as shown below:
Public Sub New()
InitializeComponent()
dpGraph.DockLeftPortion = 225
dpGraph.BringToFront()
Dim frmT As frmGraphTools = New frmGraphTools()
Dim frmG As frmGraph = New frmGraph()
AddHandler frmT.UpdateGraph, AddressOf frmG.RefreshGraph
frmT.ShowHint = DockState.DockLeft
frmT.CloseButtonVisible = False
frmT.Show(dpGraph)
frmG.ShowHint = DockState.Document
frmG.CloseButtonVisible = False
frmG.Show(dpGraph)
End Sub
Within the frmGraphTools class I have the following delegate, event, and button click event defined:
Public Delegate Sub GraphValueChanged(ByVal datum As Date)
Public Event UpdateGraph As GraphValueChanged
Private Sub btnSaveMach_Click(sender As Object, e As EventArgs) Handles btnSaveMach.Click
RaiseEvent UpdateGraph(dtpJobDate.Value.ToString())
End Sub
Within the frmGraph class I have the following Sub defined:
Public Sub RefreshGraph(ByVal datum As Date)
CreateGraph(datum)
frmGraphBack.dpGraph.Refresh()
End Sub
I have a ZedGraph control on the frmGraph form that is supposed to be refreshed/redrawn upon the button click as defined on frmGraphTools. Everything seems to working, the RefreshGraph Sub within frmGraph is being executed and new data is pushed into the ZedGraph control however, the control never updates. What must be done to get the frmGraph form or the ZedGraph control to update/refresh/redraw properly?
Pass the reference to RefreshGroup method from the correct instance of frmGraph
AddHandler frmT.UpdateGraph, AddressOf frmG.RefreshGraph
also this call should be flagged by the compiler because you are passing a string instead of a Date
RaiseEvent UpdateGraph(dtpJobDate.Value.ToString())
probably you have Option Strict Off

SerialPort and Control Updating in MDI form

As my title implies i have the following problem, i am receiving data from serial port and i update a richtextbox in a MDI Form with the control.invoke method
(Code in SerialPort.DataReceived Event)
If myTerminal.Visible Then
myTerminal.MyRichTextBox1.Invoke(New MethodInvoker(Sub()
myTerminal.MyRichTextBox1.AppendText(dataLine & vbCrLf)
End Sub))
End If
But as a mdi form it has the ability to close and reopen. So when the serialport is sending data to richtextbox and the user click the close button and the form gets disposed. Then the error "Invoke or BeginInvoke cannot be called on a control until the window handle has been created."... Any Idea????
My regards,
Ribben
That code is not in the SerialPort.DataReceived event it is in the event handler. (Yes, I'm nitpicking, but it points to a solution.) The best thing to do is have the form that owns myTerminal add the handler when it is created and remove the handler when it closes.
Thank you for your answer but unfortunately that's not the solution. First of all my SerialPort Class must inform 2 Forms (Form with richtextbox, Form with Listview) and another class which is responsible for drawing (Unmanaged Directx 9.0c about 4 Forms), so to implement right the serialport class i have made my own events. Again to the problme, it caused because the Serialport.DataReceived everytime it occurs creates a thread in the threadpool and when i dispose the form simply it's too slow to catch up with all the threads and so there is at least one thread which invokes the control which is already disposed!
As a temp solution i came up with (The Below code is in the TerminalForm Class which inherits Form):
Private VisibleBoolean As Boolean = False
Private Index As Integer = 0
Private Sub DataToAppend(ByVal _text As String)
If VisibleBoolean Then
Me.MyRichTextBox1.Invoke(New MethodInvoker(Sub()
Me.MyRichTextBox1.AppendText(_text & vbCrLf)
End Sub))
ElseIf Index = 1 Then
Index = 0
myDispose()
RemoveHandler myserialport.DataToSend2, AddressOf DataToAppend
End If
End Sub
Private Sub Me_Activated(ByVal sender As Object, ByVal e As EventArgs) Handles Me.Activated
VisibleBoolean = True
AddHandler myserialport.DataToSend2, AddressOf DataToAppend
End Sub
Private Sub myDispose()
If Index = 0 And Not Me.IsDisposed Then
Me.Invoke(New MethodInvoker(Sub()
MyBase.Dispose(True)
End Sub))
End If
End Sub
Protected Overrides Sub Dispose(ByVal disposing As Boolean)
End Sub
Protected Overrides Sub OnFormClosing(ByVal e As System.Windows.Forms.FormClosingEventArgs)
Index = 1
VisibleBoolean = False
End Sub
I know i don't like either but at least it's working!
Anyother improvement or suggestion is more

Generate dialog result from a custom message box class

I am developing a custom messagebox class like the following-
Public Class MyCustomMsgBox
Private MyForm As Form = New Form
Private lblHeadline As Label = New Label
Private lblMessageBody As Label = New Label
Private btnNo As Button = New Button
Private btnOk As Button = New Button
Private btnYes As Button = New Button
Public Sub New(ByVal Message As String)
With MyForm
.Width = 438
.Height = 214
.Controls.AddRange(New Control() {lblHeadline, lblMessageBody, btnNo, btnYes, btnOk})
End With
End Sub
Public Shared Function ShowErrorMsg(ByVal ErrorMessage As String) As Windows.Forms.DialogResult
Dim obj As MyCustomMsgBox = New MyCustomMsgBox(ErrorMessage)
obj.MyForm.ShowDialog()
End Sub
Public Shared function ShowSuccessMsg(ByVal SuccessMessage As String) As Windows.Forms.DialogResult
'some code
End Sub
Public Shared Function AskQuestions(ByVal Questions As String) As Windows.Forms.DialogResult
'some code
End Sub
Public Shared Function ShowExceptions(ByVal ExMessage As String) As Windows.Forms.DialogResult
'some code
End Sub
'Private Sub btnNo_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnNo.Click
' Windows.Forms.DialogResult.No()
'End Sub
End Class
Those functions are designed with related graphics, color, title and heading.
btnOk will return DialogResult.Ok, btnNo will return DialogResult.No and btnYes will return DialogResult.Yes
How do i return the dialog result with those functions?
How do i know that which button is pressed?
i dont know how to handle a button click event in a formless class.
Would you give me the idea?
Thank you in advance.
SKPaul
Start with the easy one. You will have to manually wire-up the events by using the AddHandler and RemoveHandler keywords this
AddHandler btnNo.Click, AddressOf btnNo_Click
btnNo is the button object. The ".Click" is the event you want captured. The AddressOf gets a pointer to the function (basically, it tells the compiler where the function is. Think of it as a different type of "handles".)
You'll have to remvoe the handler when your done, by doing this.
RemoveHandler btnNo.Click, AddressOf btnNo_Click
To set the Dialog Results, the form must be called via ShowDialog. You Simple set the DialogResults property of the form. I'd do it in the form.closing event.
me.DialogResult = Windows.Forms.DialogResult.OK
Me.DialogResult = Windows.Forms.DialogResult.Abort
Me.Close()
and it will return result Abort

vb.net how do I make an application that only has a notifyicon and no windows form?

I want to make an application that only has a notifyicon and doesn't have any visible window form when it starts up. I see some example sort of like what I want to do for c#, but I don't see how to do this in vb.net project.
A form is not strictly necessary. You can instantiate a NotifyIcon and use that without creating a form:
Public Class AppContext
Inherits ApplicationContext
Private notifyIcon As NotifyIcon
Private appActive As Boolean
Public Sub New()
AddHandler Application.ApplicationExit, AddressOf OnApplicationExit
notifyIcon = New NotifyIcon()
notifyIcon.Icon = My.Resources.ActiveIcon
notifyIcon.Text = "The app is active."
AddHandler notifyIcon.MouseClick, AddressOf OnIconMouseClick
appActive = True
notifyIcon.Visible = True
End Sub
Private Sub OnApplicationExit(ByVal sender As Object, ByVal e As EventArgs)
If notifyIcon IsNot Nothing Then
notifyIcon.Dispose()
End If
End Sub
Private Sub OnIconMouseClick(ByVal sender As Object, ByVal e As MouseEventArgs)
If e.Button = MouseButtons.Left Then
appActive = Not appActive
notifyIcon.Icon = If(appActive, My.Resources.ActiveIcon, My.Resources.InactiveIcon)
notifyIcon.Text = If(appActive, "The app is active.", "The app is not active.")
Else
If MsgBox("Do you want to Exit?", MsgBoxStyle.YesNo) = MsgBoxResult.Yes Then
notifyIcon.Visible = False
ExitThread()
End If
End If
End Sub
End Class
And then start your app from a Sub Main:
Public Module EntryPoint
Public Sub Main()
Dim ctx As New AppContext()
Application.Run(ctx)
End Sub
End Module
Just put the form transparent and resize it to 1x1..
And add a notifyicon..
And on the Form Load Event do this:
NotifyIcon.Visible = True
Then make what ever you want..
You can create a context menu strip (A Menu when you right click on it )
PS: If you do it, You need to go on the NotifyIcon properties and set the Context Menu Strip to that you created..
Hope it helped you..