I am converting a C# program to VB,Net. The conversion created the following line:
Me.axWebBrowser1.DocumentCompleted += New Windows.Forms.WebBrowserDocumentCompletedEventHandler(AddressOf Me.axWebBrowser1_DocumentComplete)
This line creates the following error message:
Error 17 'Public Event DocumentCompleted(sender As Object, e As System.Windows.Forms.WebBrowserDocumentCompletedEventArgs)' is an event, and cannot be called directly. Use a 'RaiseEvent' statement to raise an event.
I can not find whether there is a RaiseEvent statement in the Systems class which I can use to raise the event. I can not do it in my derived class.
You're not trying to raise the event, you're trying to subscribe to it.
In VB.NET this is done using an AddHandler statement:
AddHandler Me.axWebBrowser1.DocumentCompleted, AddressOf Me.axWebBrowser1_DocumentComplete
Also a statement isn't located inside a class, namespace or library. A statement is a line, or multiple lines, of code that goes together. So, not related to your problem, but you can read about the RaiseEvent statement.
Related
I have extended on the FileSystemWatcher class to incorporate a FolderCount monitor and FolderEmpty monitor that raise events if a folder reaches a specified amount of files or if a folder returns to an empty status. I seem to have this working and I'm getting events raised when these conditions occur.
However, my problem is that when my FileSystemWatcher first initializes, it automatically goes in to check the folder contents of the specified folder to get a file count. If the limit is already reached, I need to raise an event immediately rather than wait for the FileSystemWatcher to report it.
Currently I can only seem to raise events by plugging into the .Created and .Deleted calls, however, because no files are getting created or deleted, I don't know how to raise my event manually.
Public Sub Initialize()
SetFolderCountStatus() 'Set the isFolderEmpty flag based on file contents
If Not isFolderEmpty Then
If options.WatchForFolderCount Then
If FileCountReached(options.FileCountToWatch) Then
RaiseEvent EventFolderCount(sender, e) 'Sender and e are never defined
End If
End If
End If
End Sub
My problem is that both sender and e are never populated with anything because they sit outside of my WatcherEventArgs.
I'm sure this can be done a better way, but I am unsure. Any help would be appreciated. Thanks
Do you actually use the sender and EventArgs in your EventFolderCount method? You can pass Me for the sender and an empty EventArgs object.
However What are the event arguments “sender” and “e” suggests attempting to raise the event isn't preferred. Instead you should have a single method that accomplishes the task and have that called in both places.
I actually resolved this by changing my EventHandler to only require a String variable, rather than EventArgs:
Public Event EventFolderCount(filename As String)
This way I could call it easily inside and outside of the FileSystemWatcher like so:
RaiseEvent EventFolderCount(filename)
Thanks #Dave Anderson for pointing me in the right direction.
I have a little problem calling the button1_click event of form1 from my form2.
When I call form1.button1_click() it gives me an error saying:
Argument not specified for parameter 'e'.
How can I fix this?
Assuming WinForms, try using this:
form1.button1_click(Nothing, Nothing)
or
form1.button1_click(form1.button1, EventArgs.Empty)
The error means the procedure you are trying to run has parameters, but you left them out in your call. The click event is looking for two parameters, sender As Object and e As EventArgs.
You may also wish to try this:
form1.button1.PerformClick()
m getting an error while using
Text1.SetFocus
the error is
invalid procedure call or argument
If you're calling this from a module, i.e. not from the form code, you need to reference the form object where text1 lays.
The other thing you may need to do is to get some persistency pills, hit F1 more often and use words in abundance when asking for help.
You need to reference Text1.SetFocus from Form_Activate and no from Form_Load -
http://www.vb6.us/tutorials/understanding-forms-vb6-tutorial
Form_Load vs. Form_Activate
In the Form_Load event, you would typically perform initialization-type tasks, as you should. However, certain types of actions cannot be performed in the Load event, due to the fact that the form is fully loaded only after the Load event completes. For one thing, printing to the form will not work when done in the Load event. In addition, if you try to set focus to a particular control on the form during the Load event, you will get the message Run-time error '5': Invalid procedure call or argument. For example, assume you had a textbox called Text1 on the form. The following code would result in that error:
Private Sub Form_Load()
' other initialization stuff
Text1.SetFocus ' causes an error
End Sub
The reason for the error is that since the form is not fully loaded, neither are any of the controls on it – and you can't set focus to a control that is not yet available.
To remedy this problem, you should use one of the other Form events, such as the Activate event. (When VB loads a form, it actually cycles through a number of events, such as: Initialize, Load, Resize, Activate, GotFocus, and Paint. Of these, Load and Activate are probably the ones most commonly used. ) Placing the code to set focus to a control will work in the Form_Activate event:
Private Sub Form_Activate()
' other statements
Text1.SetFocus ' no problem here
End Sub
A caution about the activate event: it will fire whenever your application switches to that form. For example, if you switch back and forth between Form1 and Form2, be aware that any code you might have in the Activate events for these forms will be executed when you switch to that form. Therefore, if you have code in the Activate event that you only want to execute "the first time", you will need to control execution with a Boolean switch. For example, in the General Declarations of your form you could define the following variable:
Private mblnFormActivated As Boolean ' will be initialized to False by default
You can then use this switch in the Activate event as follows:
Private Sub Form_Activate()
If mblnFormActivated Then Exit Sub
' statements you only want to execute once, including the following
' statement to turn the switch on:
mblnFormActivated = True
End Sub
I need to pass an event as a parameter to a function. Is there a way of doing this?
The reason is that I have a sequence of two lines of code that is littered all over my program, where I dynamically remove the handler to an event, and then set the handler again. I'm doing this for several different events and event handlers, so I've decided to write a function that does this.
As an example, let's say I have a combobox in my code called combobox1, and I have the handler called indexChangedHandler. In several places of my code, I have the following two lines:
RemoveHandler combobox1.SelectedIndexChanged, AddressOf indexChangedHandler
AddHandler combobox1.SelectedIndexChanged, AddressOf indexChangedHandler
Now, I don't want to keep on repeating the above two lines of code (or similar) all over my program, so I'm looking for a way to do this:
Private Sub setHandler(evt As Event, hndler As eventhandler)
RemoveHandler evt, hndler
AddHandler evt, hndler
End Sub
so that everywhere where those two lines of code(or similar) occur in my program, I can just replace them with:
setHandler(combobox1.SelectedIndexChanged, AddressOf indexChangedHandler)
So far, the "evt as Event" part of the argument of the setHandler function is giving an error.
P.S: I've asked this question on a couple of other forums and keep getting asked why I would want to set the handler immediately after removing it. The reason is because dynamically adding an event handler n-times causes the handler to be executed n-times when the event occurs. To avoid this, that is, to ensure that the handler is executed just once when the event occurs, I first remove the handler each time I want to add the handler dynamically.
You might be asking why the handler would be added several times in the first place... The reason is because I add the handler only after a particular event, say E1, in my form has occurred (I add the handler within the handler of event E1). And event E1 can occur several times within my form. If I do not remove the handler each time before adding it again, the handler gets added and thus executed several times.
Whatever the case, the processing occurring within the function is not of ultimate importance to me at this time, but rather just the means of passing an event as a parameter.
Of course you can pass events around... Well you can pass Action(Of EventHandler) which can do what you want.
If you have a class that defines an event like so:
Public Class ComboBox
Public Event SelectedIndexChanged As EventHandler
End Class
Given an instance of ComboBox you can then create add & remove handler actions like so:
Dim combobox1 = New ComboBox()
Dim ah As Action(Of EventHandler)
= Sub (h) AddHandler combobox1.SelectedIndexChanged, h
Dim rh As Action(Of EventHandler)
= Sub (h) RemoveHandler combobox1.SelectedIndexChanged, h
(Now, this is VB.NET 4.0 code, but you can do this in 3.5 using AddressOf and a little more mucking about.)
So if I have a handler Foo:
Public Sub Foo(ByVal sender as Object, ByVal e As EventArgs)
Console.WriteLine("Over here with Foo!")
End Sub
And a Raise method on ComboBox I can now do this:
ah(AddressOf Foo)
combobox1.Raise()
rh(AddressOf Foo)
This writes the message "Over here with Foo!" as expected.
I can also create this method:
Public Sub PassActionOfEventHandler(ByVal h As Action(Of EventHandler))
h(AddressOf Foo)
End Sub
I can pass around the event handler actions like so:
PassActionOfEventHandler(ah)
combobox1.Raise()
PassActionOfEventHandler(rh)
Which again writes the message "Over here with Foo!".
Now, one issue that might be a problem is that you can accidentally swap the add and remove event handler delegates in code - after all they are the same type. So it is easy to just define strongly-typed delegates for the add and remove actions like so:
Public Delegate Sub AddHandlerDelegate(Of T)(ByVal eh as T)
Public Delegate Sub RemoveHandlerDelegate(Of T)(ByVal eh as T)
The code to define the delegate instances doesn't change except for the delegate types:
Dim ah As AddHandlerDelegate(Of EventHandler)
= Sub (h) AddHandler combobox1.SelectedIndexChanged, h
Dim rh As RemoveHandlerDelegate(Of EventHandler)
= Sub (h) RemoveHandler combobox1.SelectedIndexChanged, h
So this now lets you be very creative. You can define a function that will take the add handler delegate, the remove handler delegate and a event handler, which will wire-up an event handler, and then return to you an IDisposable that you can use later to remove the handler without needing to retain a reference to the event handler. This is handy for using Using statements.
Here's the signature:
Function SubscribeToEventHandler(
ByVal h as EventHandler,
ByVal ah As AddHandlerDelegate(Of EventHandler),
ByVal rh As RemoveHandlerDelegate(Of EventHandler)) As IDisposable
So given this function I can now do this:
combobox1.Raise()
Using subscription = SubscribeToEventHandler(AddressOf Foo, ah, rh)
combobox1.Raise()
combobox1.Raise()
End Using
combobox1.Raise()
And this writes the message "Over here with Foo!" only twice. The first and last Raise calls are outside of the subscription to the event handler.
Enjoy!
You cannot pass events around. Event isn't a type, it's a keyword that defines a pair of methods, add and remove, used to change the state of the event member of the class. They are very much like properties in this respect (which have get and set methods).
Like properties, the add and remove methods can do whatever you want. Typically, these will do nothing more than maintain a delegate instance, which itself is a MulticastDelegate or in other words, a list of delegates which are getting called one by one if the event is raised.
You can clearly see this structure in C#, where it is not masked with AddHandler/RemoveHandler, but you directly edit the list of associated handlers: myObject.Event += new Delegate(...);.
And, again like properties, being a member of a class that actually abstracts two different methods, it's not possible to literally pass an event as an object.
There are a number of different meanings for the term "event", depending upon context. In the context you are seeking, an Event is a matched pair of methods that will effectively add or remove a delegate from a subscription list. Unfortunately, there's no class in VB.NET to represent a method address without an attached object. One could probably use reflection to fetch the appropriate methods, and then pass those methods to a routine that would call them both, but in your particular situation that would probably be silly.
The only situation where I can see that it might be useful to pass an event in the sense you describe would be for something like an IDisposable object which subscribes to events from longer-lived objects, and which needs to unsubscribe all of its events when it is disposed. In that case, it may be helpful to use reflection to fetch the remove-delegate method for each event, and then call all of the remove-delegate methods when an object is disposed. Unfortunately, I know of no way to represent the events to be retrieved except as string literals, whose validity could not be checked until run-time.
At the beginning of a VB .NET function I remove an event handler and add it again at the end of the function because the function's code would trigger those events and I do not want them to trigger for the duration of the function. This usually works, but I have run into a few situations where the event still gets called even though I have already removed it. Sometimes removing it twice at the beginning of the function fixes it, but other times no matter how many times I remove it, it still fires. Any ideas on what could be causing this?
Edit
The code is in a Form that has a virtual mode datagridview. I want to run some operations that will trigger the CellValueNeeded event for the datagridview without that event being fired (because it will interfere).
Public Sub DoEventRaisingStuff()
RemoveHandler grid.CellValueNeeded, AddressOf grid_CellValueNeeded
'Do things that would trigger CellValueNeeded
AddHandler grid.CellValueNeeded, AddressOf grid_CellValueNeeded
End Sub
Removing the handler multiple times does not prevent the event from firing, so it doesn't seem to be added multiple times somewhere else by accident.
Is there a way to find out what event handlers are active?
If the event handling code is being called then one of two things is happening:
You aren't removing the event handler.
You are adding the event handler multiple times. This is the more usual case.
In the past the only way I've been able to spot 2. is to find all the places where the event handler is added (hopefully only one or two) and put break points on those lines. I've then run the application under the debugger and found that it breaks more times than I expect. I use the call stack to work out why - it's always me putting the add code in the wrong place (on a button press rather than on form instantiation for example).
You can do the same with the removal code. Count the number of times each break point is hit and if they're not the same work back up the call stack to see if you can work out why.
Use class scoped flag in the function and check the flag in the event handler.
i.e.:
Private RunFunction as Boolean = False
...
Private Sub MyEvent(e as system.eventargs) handles myObject.Method
If RunFunction Then
...
End If
End Sub
...
Private Sub MyFunction()
RunFunction = False
...
RunFunction = True
End Sub