User Control Event against Form Control Event - vb.net

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

Related

Equivalent of SelectionChangeCommitted on DateTimePicker?

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

Which event on a Control level is equivalent to Form.Load event?

I am trying to create multiple custom controls in my application, For example:
Public Class CbsDataGridView
Inherits DataGridView
'...
Private Sub CbsDataGridView_Enter(sender As Object, e As EventArgs) Handles MyBase.Enter
'Code here omitted, not related to the question.
LoadGrid()
End Sub
Private Sub LoadGrid()
'Code here omitted, not related to the question.
End Sub
'...
End Class
Public Class CbsDateTimePicker
Inherits DateTimePicker
Private Sub CbsDateTimePicker_Enter(sender As Object, e As EventArgs) Handles MyBase.Enter
'Code here omitted, not related to the question.
End Sub
End Class
When adding these controls to a new empty form. Here's the two scenarios I've faced:
Scenario 1:
- Drag And Drop CbsDateTimePicker into the form
- Drag And Drop CbsDataGridView into the form
- Run Application - Load The New Form
- CbsDateTimePicker_Enter event fires.
- CbsDataGridView_Enter event doesn't fire.
Scenario 2:
- Drag And Drop CbsDataGridView into the form
- Drag And Drop CbsDateTimePicker into the form
- Run Application - Load The New Form
- CbsDataGridView_Enter event fires.
- CbsDateTimePicker_Enter event doesn't fire.
I think I miss-understood the Enter event of Controls.
What I am looking for is an event that acts like the Form.Load event which will fire when the form containing the control loads.
Is there a direct way to implement this functionality? Or should I be looking for another way?
Enter event will fire whenever the control activates by using mouse or keyboard. Also whenever you set the active control of the form by code, the event will fire.
Controls don't have a Load event similar to Load event of the Form, but they have a virtual OnCreateControl which is called when the control is first created. You can override it and add custom logic to that method, or even raise a custom Load event for your control.

Windows form actions like close through custom user control code?

I have made a title bar (custom user control) that contains five controls. They are all labels but each one do different "job".
For example, one of them is an exit form button. If I put a click event into my custom user control's code, for example...
Private Sub ExitButton_Click(sender As Object, e As EventArgs) Handles ExitButton.Click
Close()
End Sub
I get this error...
BC30451 'Close' is not declared. It may be inaccessible due to its protection level.
On the other hand I can't put it into my project's code cause it can't find ExitButton as "isolated" control and do close().
Any suggestions? I also want to do the same thing with minimize, maximize etc.
Let me guess; your button is in the user control. You try to call Close() on the UserControl class, which obviously is not a window and does not have it.
There are three solutions:
Use the ParentForm property and call Close() on it (e.g. ParentForm.Close()). Easy but not too flexible; if you want to do other things than those which are implemented in the Form base class (like Close()), e.g. specific to the main form, you would have to cast it first and check if it's really the form you thought of. Also, all those things would need to be exposed with Public or Internal, don't expose what you don't have to expose.
You pass the Form to the UserControl. Horrible because passing stuff around just ends up in spaghetti code.
Better, raise an event by the UserControl which you handle in the form the UserControl is on. That's probably the most flexible approach.
Here's a small code example solving this with an event:
Open the code of the UserControl and add an event signature and raise that event when you click the button:
Public Class MyUserControl
Public Event ButtonClicked(sender As Object, e As EventArgs)
Private Sub MyButton_Click(sender As Object, e As EventArgs) Handles MyButton.Click
RaiseEvent ButtonClicked(sender, e)
End Sub
End Class
Then, in your Form, attach to the ButtonClicked event of the UserControl:
Public Class MyForm
Private Sub MyUserControl1_ButtonClicked(sender As Object, e As EventArgs) Handles MyUserControl1.ButtonClicked
Close()
End Sub
End Class
If you re-use the event for multiple buttons, you can check which button it is through the sender passed to the event. (Of course this can be optimized by just passing a casted Button instance as the event parameter, this is just a simple example).
Where did you get "close" from? You exit an application with application.exit()
If you want to close Application you can use:
Application.Exit()
If you want to close Form:
Me.Close()
To close the form you use me.
me.close

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.

Better way to kill a Form after the FormClosing event is overridden to hide rather than close?

I have a simple Windows Form that hosts property controls at runtime. When the user clicks Close [X] I want to keep the window and its contents alive rather than killing it by handling the FormClosing event, canceling the event and simply hiding the form.
That's fine but at close of the application I need to actually close the window. I implemented the below but it feels kludgey. Is there a simpler, more clever way to handle this situation? (The form's controller calls KillForm explicitly after it receives a closing event from the main window.)
Friend Class HostForm
Private _hideInsteadOfClosing As Boolean = True
Private Sub HostForm_FormClosing(ByVal sender As Object, ByVal e As FormClosingEventArgs) _
Handles Me.FormClosing
If _hideInsteadOfClosing Then
Me.Hide()
e.Cancel = True
End If
End Sub
Public Sub KillForm()
_hideInsteadOfClosing = False
Me.Close()
End Sub
End Class
You can examine the value of the CloseReason property of the event args object. If it is UserClosing, hide the form, otherwise close it. For all possible values of this property, check the CloseReason enumeration documentation.