How does one override the OnShown Event of a form when creating a form programmatically? - vb.net

I am trying to get a sound to play when a form is first shown (rather like a standard message box does for want of a better example). If using a standard form added through the designer I would generally do this by overriding the standard onshown event and then go on to call MyBase.OnShown(e)
The problem I've hit now is that the form is being created programmatically (Dim myForm as new Form etc) and as such I seem not to be able to use AddHandler to override this event. I don't doubt that I'm doing this in entirely the wrong way, but I'd appreciate any advice that can be offered. I would prefer advice from the perspective of VB.net, but I can just about muddle through in C#.

Form.OnShown is not an event. Rather, it is a method of the Form class which raises the form's Shown event. Here's the MSDN article that explains the OnShown method:
http://msdn.microsoft.com/en-us/library/system.windows.forms.form.onshown.aspx
When you are making a derived class by using the form designer, you can override the OnShown method, but when you are simply accessing a form through its public interface, you need to use the Shown event instead. You can add an event handler for that event like this:
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
Dim f As Form1 = New Form1()
AddHandler f.Shown, AddressOf f_Shown
f.Show()
End Sub
Private Sub f_Shown(ByVal sender As Object, ByVal e As EventArgs)
End Sub

Since the form doesn't exist in code, you would actually have to call the event then.
Try writing out the showing code first:
Public Sub form_Showing(ByVal sender As Object, e As EventArgs)
// play sound
End Sub
then when you create your form, you add the handler of the event:
Dim f As New Form
AddHandler f.Shown, AddressOf form_Showing
f.Show()

Related

How to call an event?

How can I call a CharAdded event when my scintilla Text window has text added. I have tried adding this:
Private Sub Scintilla_CharAdded(ByVal sender As System.Object, ByVal e As ScintillaNet.CharAddedEventArgs) Handles Scintilla1.CharAdded
CType(TabControl1.SelectedTab.Controls.Item(0), Scintilla).AutoComplete.Show()
End Sub
But it only calls when the Scintilla window is implemented on my form in the design file. However I needed to work when I implement it like this:
Dim TextInput As New Scintilla
So is it possible to call this event when it is not placed directly in my designer?
Thanks.
If you want to use the Handles keyword, you can do so by defining your TextInput as a field of your form (a variable at the form-level, outside of any method). You then need to add the WithEvents modifier to the variable declaration, like this:
Public Class MyForm
Private WithEvents TextInput As New Scintilla
Private Sub Scintilla_CharAdded(sender As Object, e As ScintillaNet.CharAddedEventArgs) Handles TextInput.CharAdded
' ...
End Sub
End Class
If you can't define it as a field of your form, then you won't be able to use the Handles keyword. In that case, you'd need to resort to using the AddHandler and RemoveHandler commands to register your event handler with the object.

Handle an event of a control defined on another form

I have a form declared as s property WithEvents. If I add Handles formServers.FormClosing to a Sub declaration it works fine, but when I want to handle an event of a control within formServers I get the following error -
'Handles' in classes must specify a 'WithEvents' variable.
How do I correctly set this up? Thanks.
Private WithEvents formServers As New formServers
Private Sub txtServers_Closing(ByVal Sender As Object,
ByVal e As EventArgs) Handles formServers.txtServers.LostFocus
Me.SetServers()
If Me.ServersError Then
Dim Ex As New Exception("Error validating Servers.")
Dim ErrorForm = New formError(Ex, 101)
End If
End Sub
The error message is fairly misleading. The Handles keyword has several restrictions, it cannot work across different classes, it needs an object reference. You must use the more universal AddHandler keyword instead.
There are some additional problems in your scenario. Never use the LostFocus event, use Leave instead. And it is very important that you subscribe the event for the specific instance of the form, using As New gets you into trouble when you display the form multiple times, an ObjectDisposedException will be the outcome. Correct code looks like this:
Private formInstance As FormServers
Private Sub DisplayFormServer()
formInstance = new FormServers
AddHandler formInstance.txtServers.Leave, AddressOf txtServers_Closing
AddHandler formInstance.FormClosed, _
Sub()
formInstance = Nothing
End Sub
formInstance.Show()
End Sub
A much more elegant approach is to expose the event explicitly in your FormServers class. Make that look like this:
Public Class FormServers
Public Event ServersLeave As EventHandler
Private Sub txtServers_Leave(sender As Object, e As EventArgs) Handles txtServers.Leave
RaiseEvent ServersLeave(Me, EventArgs.Empty)
End Sub
End Class
The problem is that you do are not specifying WithEvents on the TextBox. Rather, you are specifying WithEvents on the Form. You can only use Handles on variables which you have declared directly with the WithEvents keyword. With the WithEvents being on the form, you will only be able to use Handles to handle events that are raised directly by the form itself. You will not be able to do so for events raised by any of its controls.
You can fix this in one of two ways. Either you can use AddHandler to register your event handler (rather than using the Handles keyword), or you can create a TextBox variable WithEvents and then set it to the appropriate TextBox object on the form, like this.
Private formInstance As New FormServers
Private WithEvents txtServers As TextBox
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles Me.Load
txtServers = formServers.txtServers
End Sub
Private Sub txtServers_LostFocus(Sender As Object, e As EventArgs) Handles txtServers.LostFocus
' ...
End Sub
The advantage of the latter approach, besides the more consistent, and possibly more elegant syntax, is that you don't have to remember to call RemoveHandler.

trigger an event from another control

I'm using vb.net and winform. I am coming across an issue which I'm pounding my head against for the past few hours.
I have a main usercontrol which I added a groupbox and inside that groupbox, added a control like this:
main usercontrol
Me.GroupBox1.Controls.Add(Me.ctlWithDropDown)
user control ctlWithDropDown
Me.Controls.Add(Me.ddList)
Private Sub ddlList_SelectionChanged(ByVal sender As Object, ByVal e As System.EventArgs) Handles ddlList.SelectionChanged
'some simple logic here to check if value changed
End Sub
The main usercontrol inherits the base class which has an event to set a value to true or false like so:
Public Event SetFlag(ByVal value As Boolean)
I want to know how I can trigger/set this boolean value from the dropdownlist when the SelectionChanged event is trigger. Any help on this issue?
Wire up an event handler for the drop down list:
AddHandler Me.ctlDropDown.SelectedIndexChanged, AddressOf ddlSelectedIndexChanged
Me.GroupBox1.Controls.Add(Me.ctlDropDown)
Make sure you create ddlSelectedIndexChanged in your control and have it fire the SetFlag Event:
Protected Sub ddlSelectedIndexChanged(ByVal sender As Object, ByVal e As EventArgs)
RaiseEvent SetFlag(True)
End Sub
I presume the me.ctlDropDown is something that you are making programmatically? If so then this sort of thing should work for you.
Public Sub Blah()
Dim ctlDropDown As New ComboBox
AddHandler ctlDropDown.SelectedIndexChanged, AddressOf IndexChangedHandler
Me.GroupBox1.Controls.Add(ctlDropDown)
End Sub
Private Sub IndexChangedHandler()
'Do whatever you need here.
End Sub
However, if this is not created at runtime should make an event handler like:
Private Sub IndexChangedHandler() Handles Me.ctlDropdown.SelectedIndexChanged
'Do whatever you need here.
End Sub

AddHandler event on dynamic button, incorrect instance properties in VB.NET

I'm new to custom classes. I have a class called 'game'. In the class, I have a method called 'addGame()' that creates a dynamic picture box called 'pBox'. After creating the control, I'm doing the following to register a click event:
AddHandler pBox.Click, AddressOf Me.launchGame
And here is launchGame:
Public Sub launchGame()
MsgBox(Me.name)
End Sub
The problem is, "Me.name" is always the most recently added instances name, not the one I clicked on.
Based on a suggestion, I also tried this:
Public Sub launchGame(ByVal sender As Object)
MsgBox(sender.name)
End Sub
But now "AddHandler pBox.Click, AddressOf Me.launchGame" says
Method 'Public Sub launchGame(sender As Object)' does not have a signature compatible with delegate 'Delegate Sub EventHandler(sender As Object, e As System.EventArgs)'
And "AddHandler pBox.Click, AddressOf Me.launchGame(Me)" says
AddressOf operand must be the name of a method without parentheses
Public Sub launchGame(ByVal sender As Object, ByVal sender as EventArgs)
MsgBox(sender.name)
End Sub
Now no errors, but the msgBox is blank.
I think the problem was that pBox was always the most recent pictureBox control. I created a control Array based on Creating Control Arrays in Visual Basic .NET and Visual C# .NET (MSDN).
Now I do the AddHandler in the AddNewpBox() method of my pBoxArray class.
I also created a list to handle the "game" class, as suggested by David Brunow. Then I'm setting the pictureBox "Tag" property to the index of the game in the "games" array.
So now my click handler looks like the following, and it seems to work great.
Public Sub pBoxClick(ByVal sender As Object, e As EventArgs)
MsgBox(games(sender.tag).name)
End Sub

Multiple event handlers for the same event in VB.NET

I've written two event handlers for the TextBox.Leave event for a TextBox1
The reason for this is that the first handler is a common one for multiple TextBox.Leave events which validates the values, and the second one is specific for the above TextBox1 which does some calculation of values.
My query is that can I know which of the two handlers will execute first when TextBox1.Leave happens?
(I know I can remove the code from the common handler to the specific one for TextBox1, but still I wish to know if there is a way.)
Thanks
As long as the event handlers are added using the AddHandler statement, the event handlers are guaranteed to be called in the same order that they were added. If, on the other hand, you are using the Handles modifier on the event handler methods, I don't think there is any way to be sure what the order will be.
Here's a simple example that demonstrates the order as determined by the order in which AddHandler is called:
Public Class FormVb1
Public Class Test
Public Event TestEvent()
Public Sub RaiseTest()
RaiseEvent TestEvent()
End Sub
End Class
Private _myTest As New Test()
Private Sub Button1_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles Button1.Click
AddHandler _myTest.TestEvent, AddressOf Handler1
AddHandler _myTest.TestEvent, AddressOf Handler2
_myTest.RaiseTest()
RemoveHandler _myTest.TestEvent, AddressOf Handler1
RemoveHandler _myTest.TestEvent, AddressOf Handler2
End Sub
Private Sub Handler1()
MessageBox.Show("Called first")
End Sub
Private Sub Handler2()
MessageBox.Show("Called second")
End Sub
End Class
I'd recommend you change to having a single handler, and detect which textbox is being left:
Private Sub txt_Leave(sender As Object, e As System.EventArgs) Handles TextBox1.Leave, TextBox2.Leave
Dim txt As TextBox = DirectCast(sender, TextBox)
If txt Is TextBox1 Then
txt.Text = "Very important textbox!"
Else
txt.Text = "Boring textbox ho hum."
End If
End Sub
This is just additional information not related to the order of execution of multiple event handlers with the same "Handles" clause.
It may be of interest, though, to those wondering about when to use a "Handles" clause and when to use the "AddHandler" statement.
Addhandler is more useful when using a UDC (User Defined Control) to execute procedures/functions or manipulate data within a form.
UDCs know nothing about the form on which they have been attached or referenced.
They are generic and written for reuse in many projects.
Take (for example) the standard "TextBox" control.
Does TextBox know anything about the form on which it rests? No.
All its properties are available to the form but none of the form's properties is available to the Textbox.
(The Textbox is a pre-supplied "UDC", and Microsoft is the User defining it)
If you want your own UDC to operate on data in the main form, you define a public event within the UDC (let's call this event "UC_Update")
It is placed on a single line at the top of the code in the UDC:
Public Event UC_Update ' UC stands for "User Control"
When your UDC (let's call it "MyControl") wants to work with data on its parent form, it can call this event (within the UDC code) with the line:
RaiseEvent UC_Update
Within the code of the form on which this control has been placed or referenced, you attach this event belonging to your UDC to an instance of your control and "point" that event to another routine written in the form.
To do this, you use the "AddHandler" directive and an associated "AddressOf" operator.
Say the process you have written to manipulate data/controls in the form along with data/methods in your UDC, is called "MyControlUpdater".
It will be in your form and look like this:
Private Sub MyControlsUpdater()
......(code).......
end sub
The "(code)" may be lines that use data in your UDC (via public properties or public Subs/Functions) or use data or controls within your form. Your UDC can now do something with items on your form.``
You would place within your form's code the following:
AddHandler MyControl.UC_Update, AddressOf MyControlsUpdater
This directive is placed somewhere after the UDC has been instantiated:
Dim oMyControl as New MyControl
AddHandler oMyControl.UC_Update, AddressOf MyControlsUpdater
If, however, the UDC is physically on your form as "MyControl1", placed there via the Visual Studio Toolbox, then you would add the Handler in your form's "Load" procedure:
Private Sub Form1_Load(sender as object, e as eventargs) Handles Me.Load
AddHandler MyControl1.UC_Update, AddressOf MyControlsUpdater
It is important to note that you cannot pass parameters via this process (when using "AddressOf"). That is why there is no "sender" in the "MyControlsUpdater" subroutine. Values that relate to your UDC
must be obtained by way of public properties in that UDC. Public UDC functions and subroutines (subs) are also available.
If you are NOT working with a UDC (often the case) but with controls created by others such as Microsoft (and therefore the internal code of the control is not available), then you use the "Handles" clause to establish how the control is handled when a certain event arises (such as a "Click" on the control).
Private sub UpdateData(sender as object, e as eventargs) handles Textbox1.Click
........(code)......
End Sub
Of course, if your own UDC doesn't need to know anything about data on your form and you are referencing it rather than adding it physically to the form, you can just use the "Handles" delegation on your own UDC as per normal:
Dim MyControl1 as New MyControl
Private Sub UpdataData(sender as object, e as eventargs) _
handles MyControl1.Text.Leave
Here the form uses data in the control (via public properties) rather than the control using data within the form.