Equivalent of SelectionChangeCommitted on DateTimePicker? - vb.net

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

Related

Remove eventhandler dynamically from a control, from another module/class Visual Basic .NET 2010

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

Preventing an event raised by a built-in function

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.

User Control Event against Form Control Event

Here is my situation, I have a user control that have the Leave event:
Private Sub MyControl_Leave(sender As Object, e As EventArgs) Handles Me.Leave
If Me.Enabled Then
MsgBox(Property1)
End If
End Sub
I have this to prevent Leave Event from triggering when the control is Disabled.
Then on my form, the control also has its own Leave event because I need to set some Properties that the Leave Event on the User Control needs.
Private Sub myControlOnForm_Leave(sender As Object, e As EventArgs) Handles MyControlOnForm.Leave
MyControlOnForm.Property1 = "value1"
End Sub
What happens is the first event that triggers is the one on the User Control and then the one on the form.
Now my problem is, as the code states above, I need the Form Event to trigger first before the User Control Event.
Is there any work around for this?
The form needs to call a procedure in the user control after it's finished handling the event. Just remove the Handles Me.leave statement, and the private statement. use the sub from your form to call the controls sub which was intended to handle the event.
Note that I've changed sender As object to sender As Mycontrol.
Sub MyControl_Leave(sender As Mycontrol, e As EventArgs)
If Me.Enabled Then
MsgBox(Property1)
End If
End Sub
Code on form
Private Sub myControlOnForm_Leave(sender As Mycontrol, e As EventArgs) Handles MyControlOnForm.Leave
MyControlOnForm.Property1 = "value1"
sender.MyControl_Leave(sender, e)
End Sub
What happens is the first event that triggers is the one on the User Control and then the one on the form.
Now my problem is, as the code states above, I need the Form Event to
trigger first before the User Control Event.
First off, you are using the incorrect term in that problem statement. It is not an event triggering order issue, but rather an issue in order in which the event handlers registered for the UserControl's Leave event execute.
.Net events are a form of syntactic sugar for the invocation of a multicast delegate. When an event is raised a delegate is invoked and the order in which the handlers are executed is the order in which they were added to the delegate. You can gain an understanding of this by working through the various "Walkthrough" tutorials located under Events (Visual Basic).
The Leave event is Raised by calling the Overridable OnLeave method inherited from the Control Class that is in the inheritance tree of the UserControl Class. It is considered bad form for a class to handle its own generated event; the preferred method is Override the method that raise the event.
In your case, you want the form that subscribes to the event to be notified first so that it can modify a property on the UserControl before some it performs some action in response to Leaving the UserControl.
Public Class UserControl1
Protected Overrides Sub OnLeave(e As EventArgs)
MyBase.OnLeave(e) ' this calls the base method that Raises the event
' all event handlers will run before the subsequent code
' executes
If Me.Enabled Then
'do something
End If
End Sub
End Class

Is it possible to select a range in a ListBox without raising the event multiple times?

The question:
I have a ListBox in my form (the class is System.Windows.Forms.ListBox), in MultiExtended selection mode. Now I want to write a Sub that selects a range of items (e.g. all items) "in one go", i.e. as if the user selected them with a drag-selection.
The naive way is of course to select them one by one, using SetSelected etc. However, the problem is that I have a handler for the SelectedIndexChanged event of this ListBox. Thus this handler will be called many times, slowing down the program.
So basically what I want is to do a range selection, and raising only one SelectedIndexChanged event. How can I do this?
What I have done and thought:
After searching, I find on MSDN that the class System.Windows.Controls.ListBox has a SelectAll method. Unfortunatedly (and strangely) there is no such kind of method for the class System.Windows.Forms.ListBox.
The roundabout idea I have now, is to detach the handler of SelectedIndexChanged, then select the items one by one, and finally re-attach the handler. But I still would like to know if there is a better way.
It is really strange if such a method is not provided, since the user can achieve this effect by a drag-selection.
Two basic ways to do it. As you mentioned, by removing the event handler:
RemoveHandler ListBox1.SelectedIndexChanged, AddressOf ListBox1_SelectedIndexChanged
ListBox1.SetSelected(0, True)
ListBox1.SetSelected(2, True)
ListBox1.SetSelected(3, True)
AddHandler ListBox1.SelectedIndexChanged, AddressOf ListBox1_SelectedIndexChanged
ListBox1_SelectedIndexChanged(ListBox1, EventArgs.Empty)
Technically, it's best to put that in a Try-Catch-Finally so if something goes wrong, the handler would always be re-attached in the finally section.
Or by using a boolean flag:
Private ignoreChange = False
Private Sub ChangeSomething()
ignoreChange = True
ListBox1.SetSelected(0, True)
ListBox1.SetSelected(2, True)
ListBox1.SetSelected(3, True)
ignoreChange = False
ListBox1_SelectedIndexChanged(ListBox1, EventArgs.Empty)
End Sub
Private Sub ListBox1_SelectedIndexChanged(sender As Object, e As EventArgs) Handles ListBox1.SelectedIndexChanged
If Not ignoreChange Then
'// do something...
End If
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.