I have come up with a solution that did solve my problem however, I am curious as to why my initial approach failed. My scenario was as described below:
I have a form that raises the event:
Public Class frmDgvLb
Public Delegate Sub ProfileChanged()
Public Event UpdateProfile As ProfileChanged
Private Sub lbDgvEdit_SelectedIndexChanged(sender As System.Object, e As System.EventArgs) Handles lbDgvEdit.SelectedIndexChanged
If UpdateHotlist(cmdStr) = True Then
If DgvName = "dgvHotlist" Then
Hotlist.LoadDGV()
ElseIf DgvName = "dgvJobProfile" Then
RaiseEvent UpdateProfile()
End If
Me.Close()
End If
End Sub
End Class
I have another form where the event is defined and handled:
Public Class frmGraph
Public Sub New()
InitializeComponent()
AddHandler frmDgvLb.UpdateProfile, AddressOf RefreshProfiles
End Sub
Public Sub RefreshProfiles()
GetProfiles(lbMach.SelectedItem, dtpJobDate.Value)
CreateGraph(dtpJobDate.Value, _machList)
zgc.Refresh()
End Sub
End Class
My problem was that the RaiseEvent UpdateProfile() would only execute once. All subsequent index changes of the listbox would not raise the event? When stepping through with the debugger, when the conditional evaluated to true, the debugger would step to the RaiseEvent line and then step to the line closing the conditional statement and the RefreshProfile Sub never executes. Again, the first time the index of the listbox changes, everything functions fine. Why is this?
You have to add the handler to the instance, not the class:
Public Sub New()
InitializeComponent()
AddHandler frmDgvLbInstance.UpdateProfile, AddressOf RefreshProfiles
End Sub
Related
I have a vb.net application which contains two forms. One is called "MapComponentForm" and another called "ComponentPropertiesForm". In the MapComponentForm I have defined an event which I want fired when the OK button is clicked. If the ComponentPropertiesForm is open, I want it to "hear" this event and act accordingly. Stepping through the code the event seems to fire as expected but the ComponentPropertiesForm seems to be oblivious to this. I've attached the code from the two forms after stripping out the non relevant code in hopes that someone can tell me why my event goes unheeded. I used the information here:
https://learn.microsoft.com/en-us/dotnet/visual-basic/programming-guide/language-features/events/walkthrough-handling-events
in constructing my own code.
Thanks for any suggestions.
Public Class MapComponentForm
Public Event PartMapped(ByRef status As Boolean)
Public Sub New(shape As Visio.Shape)
' This call is required by the designer.
InitializeComponent()
' Add any initialization after the InitializeComponent() call.
_shape = shape
End Sub
Private Sub OkButton_Click(sender As Object, e As EventArgs) Handles OkButton.Click
'If the component properties window is open, refresh it
If Application.OpenForms().OfType(Of ComponentPropertiesForm).Any Then
RaiseEvent PartMapped(True)
End If
end sub
end class
Public Class ComponentPropertiesForm
Private WithEvents mPartMap As MapComponentForm
Private Sub ComponentPropertiesForm_Load(sender As Object, e As EventArgs) Handles Me.Load
mPartMap = New MapComponentForm(Nothing)
End Sub
Private Sub mPartMap_PartMapped(ByRef status As Boolean) Handles mPartMap.PartMapped
If status = True Then
MsgBox("something got mapped")
End If
End Sub
end class
This is my situation, there are 2 Classes and my main form Form1:
Class1: has a method doSomethingAndCall(callback) which creates a new thread
Class2: has dynamic created controls with a button that fires Class1.doSomethingAndCall(newCallback)
in code it looks like this (it starts at Class2.Button_Click):
Class Class1
public shared sub doSomethingAndCallAsync(state as object)
Console.WriteLine(Form1.InvokeRequired) 'output: false
Console.WriteLine(Form1.IsHandleCreated) 'output: false
Form1.Invoke(state.callback) 'throws System.InvalidOperationException
end sub
public shared sub doSomethingAndCall(callback as object)
System.Threading.ThreadPool.QueueUserWorkItem(AddressOf doSomethingAndCallAsync, New With {.callback = callback})
end sub
End Class
Class Class2
Public Delegate Sub doSomethingDelegate()
Public Sub doSomething()
Console.WriteLine("success!")
End Sub
Public Sub Button_Click(ByVal sender As Object, ByVal e As System.EventArgs)
Class1.doSomethingAndCall(New doSomethingDelegate(AddressOf doSomething))
End Sub
End Class
The exact exception I get is:
Invoke or BeginInvoke cannot be called on a control until the window handle has been created
and as I can see the console.WriteLine in line 4 shows me that the form is realy not created. So I added this handlers, and now it get's really confusing:
Private Sub Form1_HandleCreated(sender As Object, e As System.EventArgs) Handles Me.HandleCreated
Console.WriteLine("Handle created") 'Output: Handle created, when running program
End Sub
Private Sub Form1_HandleDestroyed(sender As Object, e As System.EventArgs) Handles Me.HandleDestroyed
Console.WriteLine("Handle destroyed") 'Will never Output!
End Sub
So it's created and never destroyed but if i click the button it's nevertheless not avaible? -Can anyone explain me what is going on and how to call a callback correct, thanks!
The instance of My.Forms.Form1 aka. Form1 will be different in each thread. You need a handle to the correct instance. Drop a button onto your Form1 and add the following code:
Public Class Form1
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Threading.Tasks.Task.Factory.StartNew(Sub() Class1.Wrong())
Threading.Tasks.Task.Factory.StartNew(Sub() Class1.Correct(Me))
End Sub
End Class
Public Class Class1
Public Shared Sub Wrong()
Debug.WriteLine(String.Format("(Other thread, wrong) InvokeRequired={0}, IsHandleCreated={1}", Form1.InvokeRequired, Form1.IsHandleCreated))
End Sub
Public Shared Sub Correct(instance As Form1)
Debug.WriteLine(String.Format("(Other thread, correct) InvokeRequired={0}, IsHandleCreated={1}", instance.InvokeRequired, instance.IsHandleCreated))
End Sub
End Class
Output
(Other thread, correct) InvokeRequired=True, IsHandleCreated=True
(Other thread, wrong) InvokeRequired=False, IsHandleCreated=False
I have a form that has a start button (to allow users to run the processes over and over if they wish), and I want to send a btnStart.Click event when the form loads, so that the processes start automatically.
I have the following function for the btnStart.Click event, but how do I actually tell Visual Basic 'Pretend someone has clicked the button and fire this event'?
I've tried going very simple, which essentially works. However, Visual Studio gives me a warning Variable 'sender' is used before it has been assigned a value, so I'm guessing this is not really the way to do it:
Dim sender As Object
btnStart_Click(sender, New EventArgs())
I have also tried using RaiseEvent btnStart.Click, but that gives the following error:
'btnStart' is not an event of 'MyProject.MyFormClass
Code
Imports System.ComponentModel
Partial Public Class frmProgress
Private bw As BackgroundWorker = New BackgroundWorker
Public Sub New()
InitializeComponent()
' Set up the BackgroundWorker
bw.WorkerReportsProgress = True
bw.WorkerSupportsCancellation = True
AddHandler bw.DoWork, AddressOf bw_DoWork
AddHandler bw.ProgressChanged, AddressOf bw_ProgressChanged
AddHandler bw.RunWorkerCompleted, AddressOf bw_RunWorkerCompleted
' Fire the 'btnStart.click' event when the form loads
Dim sender As Object
btnStart_Click(sender, New EventArgs())
End Sub
Private Sub btnStart_Click(sender As Object, e As EventArgs) Handles btnStart.Click
If Not bw.IsBusy = True Then
' Enable the 'More >>' button on the form, as there will now be details for users to view
Me.btnMore.Enabled = True
' Update the form control settings so that they correctly formatted when the processing starts
set_form_on_start()
bw.RunWorkerAsync()
End If
End Sub
' Other functions exist here
End Class
You should send a button as sender into the event handler:
btnStart_Click(btnStart, New EventArgs())
Steps in involved in raising an event is as follows,
Public Event ForceManualStep As EventHandler
RaiseEvent ForceManualStep(Me, EventArgs.Empty)
AddHandler ForceManualStep, AddressOf ManualStepCompletion
Private Sub ManualStepCompletion(sender As Object, e As EventArgs)
End Sub
So in your case, it should be as below,
btnStart_Click(btnStart, EventArgs.Empty)
Just Call
btnStart.PerformClick()
You are trying to implement a bad idea. Actually, you have to make a subroutine to accomplish these kind of tasks.
Private Sub btnStart_Click(sender As Object, e As EventArgs) Handles btnStart.Click
call SeparateSubroutine()
End Sub
private sub SeparateSubroutine()
'Your code here.
End Sub
And then whereever you want to call the btnStart's click event, just call that SeparateSubroutine. This should be a correct way in your case.
You can subclass the button and make its OnClick Method public as I described here.
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
i've a app that starts from a sub in a module, do a few things, and then load the form.
But it doesn't work :/
Here we execute dBase.AddTemporalFilepath
module.vb
Public dBase As New Core.clsDatabase
Public Sub Main()
FurBase.Directory = My.Application.Info.DirectoryPath
If appMutex.WaitOne(TimeSpan.Zero, True) Then
ShowUploader()
End If
Dim returnValue As String()
returnValue = Environment.GetCommandLineArgs()
If returnValue.Length > 1 Then
If My.Computer.FileSystem.FileExists(returnValue(1).ToString) Then
dBase.AddTemporalFilepath(returnValue(1).ToString)
End If
End If
End Sub
Private Sub ShowUploader()
Application.EnableVisualStyles()
Application.Run(frmUploader)
End Sub
We raise the event TempFilepathAdded
clsDatabase.vb
Public Class clsDatabase
Public Event TempFilepathAdded()
Public Function AddTemporalFilepath(ByVal filepath As String)
...
RaiseEvent TempFilepathAdded()
...
End Function
End Class
We catch the event
form.vb
Private Sub form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
AddHandler dBase.TempFilepathAdded, AddressOf TempFilepathAddedHandler
End Sub
Private Sub TempFilepathAddedHandler()
MsgBox("Event raised")
End Sub
Any Idea?
More info:
The event is raised when the form is closed.
The line "Application.Run(frmUploader)" pauses your program until the Window closes. Basically it hijacks the main thread to handle stuff like users clicking buttons.
Normally your Main function should look like this:
Setup
Application.Run
Clean-up
Sorry, but it looks like its time to reorganize your code.