I have a MouseEnter event which currently handles some custom controls on my form. The program is a card game. I have a collection (handCards) that gets populated when the user draws a card and then it adds the latest card to the form. This collection holds cards of various custom types, all which inherit from picturebox. Drawing the cards from the deck and adding them to the form works fine. The trouble I am having is that at runtime, after a card is drawn and added to the form, I've created an addhandler line of code to have those cards respond to my MouseEnter event, but my addhandler line of code is telling me that MouseEnter is not an event of object. How can I get around this so that after a card is drawn and added to the form, when the mouse enters the new custom control, my MouseEnter event fires? Here's one of the many things I've tried and what I think should be the simplest and easiest that should work.
deck.DrawCard()
AddHandler handCards(handCards.Count).MouseEnter, AddressOf Cards_MouseEnter
P.S. the MouseEnter event works fine for custom controls that are on the form prior to runtime and all it does is take the image of the control and enlarge it by placing the image to a bigger card on the form.
I am supposing your handCards Collection is an Object Collection. Try casting it to the proper type using CType, something like this:
AddHandler CType(handCards(handCards.Count), PictureBox).MouseEnter, AddressOf Cards_MouseEnter
as #Jason mentioned using the handCards.Count as an index will not work because it is the total number of items where as your index is zero based and will be one less than the Count.
so handCards(handCard.Count) should be handCards(handCards.Count -1)
So this is how I fixed it, in case anyone comes across this post. Made a separate Sub to do the AddHandler. After the program draws a card, it calls upon this method, which then adds the MouseEnter handler I need. The ByVal was key. I originally thought I was supposed to use ByRef, but no. MouseEnter is an event of control, but apparently not Object, so now it works.
Public Sub addHandlers(ByVal inputObject As Control)
AddHandler inputObject.MouseEnter, AddressOf Cards_MouseEnter
End Sub
You could use a generic collection to avoid type casting.
Private handCards As System.Collections.Generic.List(Of PictureBox) _
= New System.Collections.Generic.List(Of PictureBox)(52)
Or you could just use an array of PictureBox objects
Private handCards(5) As PictureBox
Remember that you'll have to initialise the collection or array though, by assigning a PictureBox object to each element of the array.
Now you can add the handler to a PictureBox element of the array since PictureBox derives from Control which implements the MouseEnter event.
deck.DrawCard()
If handCards.Count > 0 andAlso handCards.Last() IsNot Nothing then
AddHandler handCards.Last().MouseEnter, AddressOf Cards_MouseEnter
End If
Your handler will look something like this
Private Function Cards_MouseEnter(sender As Object, e As System.EventArgs) As Object
' Handle mouse events here
End Function
Luckily I was working around and I managed to do successfully this solution.
First Add Event Handler Method Where ever you Wish, For Test I have added this Function at Button_Click
addHandlers(Label1) 'Label one is the control on which I have to attach Mouse Events (Enter,LEave)
Now "addHandlers" function implementation
Public Sub addHandlers(ByVal obj1 As Control)
AddHandler obj1.MouseEnter, AddressOf MouseEventArgs
AddHandler obj1.MouseLeave, AddressOf _MouseLeave
End Sub
Now the Mouse Events:
Private Function _MouseLeave(ByVal sender As Object, ByVal e As System.EventArgs) As Object
Try
Me.Cursor = Cursors.Default
Catch ex As Exception
End Try
End Function
Private Function MouseEventArgs(ByVal sender As Object, ByVal e As System.EventArgs) As Object
Try
Me.Cursor = Cursors.Hand
Catch ex As Exception
End Try
End Function
Related
I have a function defined in a module which updates the passed byref Combobox and Listbox controls.
for simplicity, say that the definition is as under
Module isDB
private sub PopulateCBO(byref CBO as combobox)
'Here I want to remove SelectedValueChanged and click events temporarily removed using
'removehandler or eventhandler.remove
End Sub
End Module
The control maybe on any form from the project.
To remove an event handler you need a reference to an object that raises an event and a delegate for a method that handles that event. You can't arbitrarily clear all handlers for an event. As such, you cannot remove event handlers in the method you've shown. You'd have to pass the event handler in as well, e.g.
Public Sub ConfigureComboBox(comboBox As ComboBox, onSelectedIndexChanged As EventHandler)
'...
RemoveHandler comboBox.SelectedIndexChanged, onSelectedIndexChanged
End Sub
You would then call that in a form like so:
ConfigureComboBox(ComboBox1, AddressOf ComboBoxes_SelectedIndexChanged)
My around turned out to be fairly simple.
I disabled the comboboox with enabled = false and then in the event handler function, I checked if it is enabled =false
Although removing and adding handlers with all their pros and cons didn't even function :)
What I mean is apart of from alternatives, I still wonder who to do that?
The solutions above did not work on my vb 2010.
Private Sub cboProgram_SelectedIndexChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles cboProgram.SelectedIndexChanged
If cboProgram.Enabled = True Then
MsgBox("Enabled, so the real event handler")
Else
MsgBox("Disabled, so a false positive")
End If
End Sub
I have some functions that need to be active only after the Form as loaded.
I had this problem with the ComboBoxes and solved it using SelectionChangeCommitted instead of SelectedValueChanged since the first one only is fired when the user is the one making the change.
Is there any equivalent event for the DateTimepicker.ValueChanged (since this one like the Select.SelectedValueChanged is fired before everything is set)?
This is a pretty common situation.
When, after the first initialization of a Form, a Control's value is modified, the associated Event(s) is/are raises, to notify the change to the subscribers.
The ComboBox SelectionChangeCommitted event is a notable exception: it's used to discriminate between a user direct input and a more generic value change in the selection.
The DateTimepicker control, like most of the other standard controls, doesn't provide a similar event.
Since the Event Handlers subscribed to have attached code that is run when an event is raised, it's a common problem to avoid that these routines are executed when the values of the Controls on a Form are modified by the code that initializes them.
There are different methods to manage the raising of events that can cause unwanted cascading effects at the wrong time. Two of the more common:
Attach the Event Handlers only after the Controls' initialization is completed, using dedicated methods that are delegated to attach/detach the handlers
Make use a boolean Field as a flag to indicate whether the code associated to the Event Handlers should be run when the event is raised.
Both
Attach the Handlers after the initialization procedures:
Public Sub New()
InitializeComponent()
InitializeDataStuff()
InitializeEverythingElse()
AttachHandlers()
End Sub
Private Sub Form_FormClosed(sender As Object, e As FormClosedEventArgs) Handles MyBase.FormClosed
RemoveHandlers()
End Sub
Protected Sub AttachHandlers()
Addhandler SomeControl.SomeEvent, AddressOf SomeHandlerMethod
Addhandler SomeOtherControl.SomeOtherEvent, AddressOf SomeOtherHandlerMethod
(...)
End Sub
Protected Sub RemoveHandlers()
Removehandler SomeControl.SomeEvent, AddressOf SomeHandlerMethod
Removehandler SomeOtherControl.SomeOtherEvent, AddressOf SomeOtherHandlerMethod
(...)
End Sub
Use a Boolean Field. The Event Handlers are attached using the Forms' designer:
private DontBotherToRunNow As Boolean = True
Public Sub New()
InitializeComponent()
InitializeDataStuff()
InitializeEverythingElse()
DontBotherToRunNow = False
End Sub
Private Sub DateTimePicker_ValueChanged(sender As Object, e As EventArgs) Handles DateTimePicker1.ValueChanged
If DontBotherToRunNow Then Return
(...)
'Do something
End Sub
I have this event handler bound to my listview
ListView1.ColumnWidthChanged
And I have this function which alters my columns' dimensions executed right after feeding my list with data.
ListView1.AutoResizeColumns
I want to know if there is a way to suspend VB from firing an event after an automatic sorting ? I mean anything but manual intervention ?
I though about setting a global variable but this takes much work and can be imprecise, also event args parameter doesn't encompass anything implicit that handles a mouse event, Is there any way through ?
This is what I have tried so far about mouse event handling:
Sub listview1mousedown(sender As Object, e As EventArgs) Handles ListView1.MouseMove
AddHandler ListView1.ColumnWidthChanged, AddressOf resizetable
End Sub
Sub listview1mouseup(sender As Object, e As EventArgs) Handles ListView1.MouseLeave
RemoveHandler ListView1.ColumnWidthChanged, AddressOf resizetable
End Sub
Is there any peer solution concerning events triggered by other means rather than mouse ?
Found it finally, ....
Appearently setting cursor event catch doesn't work literally at the header's level, the function doesn't fire any event when the mouse gets off the higher bound of subitem collection's container, therefor I managed te set focus event listener to catch mouse focus on the listview, this is enough user defined behavior for me:
I set:
AddHandler CType(list, ListView).GotFocus, Sub()
AddHandler CType(list, ListView).ColumnWidthChanged, AddressOf resizetable
Debug.Print("handler on")
End Sub
AddHandler CType(list, ListView).LostFocus,Sub()
RemoveHandler CType(list, ListView).ColumnWidthChanged, AddressOf resizetable
Debug.Print("handler off")
End Sub
Then i wrote in my Main():
Public Function resizetable( table As listview, e As columnwidthchangedeventargs)
'... core of the event handler
Debug.Print("Listview column width changed")
End Function
So i ran my app in debug mode and proceeded to adjust my column size manually, then I saw this in my debug console:
handler on
Listview column width changed
handler off
I ran this inside main():
list.AutoResizeColumns(ColumnHeaderAutoResizeStyle.ColumnContent)
Nothing appeared in my output !!!! I think I got it right. thanks to everyone for their deliberate advices.
I have more than 30 picture box in a windows form named
PictureBox1
PictureBox2
PictureBox3
.....so on
I am having to write the same procedure for click function for every single one of them.
I just want to be able to write a single handler that would handle the click events for all the PictureBox and call the same function.
For a rough technical example.. Sorry for the JavaScript Format
picturebox.onclick(){
var a=get_index(this); //gets address of the clicked picturebox
somefunction(a);
}
If you are using the Handles keyword, which is the default, typical way to handle events from controls, you can add multiple event names to the Handles clause, for instance:
Private Sub OnClick(sender As Object, e As EventArgs) Handles PictureBox1.Click, PictureBox2.Click, PictureBox3.Click
' Handles click events from PictureBox1, PictureBox2, and PictureBox3
End Sub
Alternatively, you can declare the event handler method without the Handles clause, and then manually attach it to the events yourself, like this:
Private Sub OnClick(sender As Object, e As EventArgs)
' ...
End Sub
' ...
AddHandler PictureBox1.Click, AddressOf OnClick
AddHandler PictureBox2.Click, AddressOf OnClick
AddHandler PictureBox3.Click, AddressOf OnClick
Or, if you have list of picture box controls, you could add the event handlers in a loop, like this:
Dim myPictureBoxes() As PictureBox = {PictureBox1, PictureBox2, PictureBox3}
For Each i As PictureBox in myPictureBoxes
AddHandler i.Click, AddressOf OnClick
Next
Or, you could access them from your form's Controls collection by name:
For i As Integer = 1 to 30
Dim c As Control = Me.Controls("PictureBox" & i.ToString())
AddHandler c.Click, AddressOf OnClick
Next
Bear in mind, however, if you do manually call AddHandler, you need to also call RemoveHandler to later detach the event handler.
It seems odd, on the surface, to have so many picture boxes like that, though. You may want to consider custom-drawing your content in a single control or look into dynamically loading the controls at run-time.
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.