Windows form actions like close through custom user control code? - vb.net

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

Related

VB: How I Can Set a Key Shortcut for a Timer/Button

How I Can Set a Key Shortcut for a Timer/Button, Basicaly I Have two Timer Events, one Timer.Stop and Timer.Start. I Want make a keyshortcut when clicked toggle's the function (Start/Stop). Please Help, I Really need it.
Here's a manner to catch the Enter key. You should be able to expand on the concept to achieve what you want.
Public Class Form1
Private Sub Form1_KeyPress(sender As Object, e As KeyPressEventArgs) Handles Me.KeyPress
If e.KeyChar = Convert.ToChar(13) Then
MsgBox("You pressed Enter")
End If
End Sub
End Class
Have fun!
Here's an edit to help you with your forms:
Let's say that you have a main form (the "outside one"), which contains one "child" window. I say "child" but it has nothing to do with inheritance, they are just forms.
Now you want a key press to be catched by the "main" form even if the focus is on the "child" form. Here's a ninja-esque way to accomplish this. It has the quality of being simple, but it's not the most elegant. Still, it'll work as intended.
In the "main" form, you need to keep track of the "child" form in a way which lets you use it's events, and a public Event:
Public Class Main
'modal variable to keep track of the child form
Private WithEvents _childForm As Form
'the rest of your main class goes here
End Class
I don't know if you have only one child Form or many, so you may consider using a List if you have more than one child Form or a variable number of them:
Private _childFormsList As New List(Of Form)
Now, every time you open a form, you have to update the modal variables in the main (I'll work on teh assumption that you have only one child Form at a time to make things easier):
_childForm = New ChildFormClass()
_childForm.Show()
'or whatever you're doing with the Form
Now, go back to this line from before:
Private Sub Form1_KeyPress(sender As Object, e As KeyPressEventArgs) Handles Me.KeyPress
and add the relevant Event from the child Form:
Private Sub Form1_KeyPress(sender As Object, e As KeyPressEventArgs) Handles Me.KeyPress, _childForm.KeyPressed
And now this Event will trigger for both Forms. You should rename the Sub, though, as it's not only Form1 (the main Form, whatever you named it) which will trigger anymore.
I'll hang around from time to time in case you have further questions.

Raising UserControl's default Click event

I've created a usercontrol.
I've placed a Button into it.
Now when I click the button, I would like to raise the default Click event.
For that, I added the following code:
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
RaiseEvent Click(sender, e)
End Sub
What am I doing wrong here?
This is the entire code of the usercontrol:
Imports System.ComponentModel
Public Class ucColorButton
<Browsable(True)>
Public Overrides Property BackColor() As Color
Get
Return Me.Button1.BackColor
End Get
Set(value As Color)
Me.Button1.BackColor = value
End Set
End Property
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
RaiseEvent Click(sender, e)
End Sub
End Class
The compiler tells me:
There's no RaiseEvent definition for the event "Click"
You don't use RaiseEvent to raise an inherited event. This is why all events should have an associated method. To raise the Click event you call the OnClick method and that is the only place that RaiseEvent is used. If you want to change the behaviour on a Click event then you override that method, otherwise you just accept the default behaviour from the base class. To see how events are properly implemented - and are implemented in the base classes you're inheriting - check this out.
Also, while it technically doesn't matter in this case, you shouldn't really be passing the e parameter from your internal event handler to your external event. You should be creating your own EventArgs object as required by your event.
Finally, if you were to be able to use RaiseEvent, it would be wrong to pass on the sender parameter too. The sender is ALWAYS supposed to be the object that raised the event. In your case, that is the user control, NOT the internal Button. Fortunately, calling OnClick will fix that. If you needed to pass on information about which child control was clicked then you should be defining your own event and passing that information via the e parameter.

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

In a VB.NET Windows Forms application: How to preserve MVC when handling Events?

I'm relatively new to Windows Forms development and my first real application has reached a point where a lot of code starts to build up in my main Form file, so I decided to restructure my project using the MVC pattern.
One major problem I have is dealing with the different control events of the form. I have several buttons, textfields, comboboxes and also a tabcontroll element which again contains different input elements and so far, every procedure for handling clicks, updates and other changes is defined in my main class.
For example:
Private Sub btnOk_Click(sender As Object, e As EventArgs) Handles btnOk.Click
some code...
End Sub
So my question is: what would be the best way to handle these events outside of my main form? I'm more familiar with building GUIs in Java where you can use ActionListeners to achieve this but I have found nothing similar for my work with Windows Forms.
To subscribe to a Control event outside of your main form class, make your control public, so you can access from another class). This can be done using the Modifier property at design-time. Then, use the AddHandler keyword to subscribe to any event programmatically.
After researching a bit more, I found that there is probably not THE correct answer to this problem but I found 2 approaches which provide a solution in the way I was looking for. In both cases, I use a controller class which is responsible for handling any user interaction from my main form.
The first approach makes use of what DmitryBabich suggested, adding a handler to the object and referencing it to a method of my controller class:
in Form1:
Dim ctrl as new Controller(Me)
AddHandler Button1.Click, AddressOf ctrl.doSomething
Controller class:
Public Class Controller
Private myForm As Form1
Public Sub New(ByVal f As Form1)
myForm = f
End Sub
Public Sub doSomething(sender As Object, e As EventArgs)
MsgBox("Button clicked.")
End Sub
End Class
For an example this simple it is not necessary to pass an instance of Form1 over to the controller but if for example I'd like to access the values of other control elements as well, I can address them by using this instance of Form1.
For example:
Public Sub doSomething(sender As Object, e As EventArgs)
MsgBox("You clicked the button, by the way: The value of TextField1 is " & myForm.TextField1.text)
End Sub
The other approach is almost identical except that here the controller knows all the relevant user control objects of the form and can handle their events directly, meaning that in the main form I have to do nothing more than create an instance of the controller. In the controller however, I have to assign every user control I want to access to its own variable as soon as the main form is loaded:
in Form1:
Dim ctrl as new Controller(Me)
Controller class:
Public Class Controller
WithEvents myForm As Form1
WithEvents button1 As Button
WithEvents button2 As Button
Public Sub New(ByVal f As Form1)
myForm = f
End Sub
Public Sub formLoad() Handles myForm.Load
button1 = myForm.Button1
button2 = myForm.Button2
End Sub
Private Sub b1Click() Handles button1.Click
MsgBox("You clicked button1!")
End Sub
Private Sub b2Click() Handles button2.Click
MsgBox("Button #2 was clicked!")
End Sub
End Class

Create And Implement a Custom Form in VB.NET

I am trying to create a customized form class (CustomBorderlessForm) in VB.NET.
My Progress So Far
I created a new Class and named it CustomBorderlessForm.vb
I then proceeded to write the following code:
CustomBorderlessForm.vb
Public Class CustomBorderlessForm
Inherits Form
Dim _form As Form = Nothing
Public Sub New(form As Form)
_form = form
MsgBox("Testing: New()")
End Sub
Protected Overrides Sub OnMouseMove(e As MouseEventArgs)
MyBase.OnMouseMove(e)
MsgBox("Testing OnMouseMove()")
End Sub
End Class
Form1.vb
Public Class Form1
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Dim form As New CustomBorderlessForm(Me)
End Sub
End Class
Results of progress
A message box displays "Testing: New()" on load
Nothing shows on mouse move
As you can see, my problem lies with the events
Questions
Is it possible to create a form object and use that instead of the pre-populating form?
If so, can I give this form custom properties, such as, a border and some boolean values (shadow...etc), just like any other custom object/class?
What am I doing wrong in my current approach?
Why isn't the OnMouseMove being overridden?
Am I initialising the class wrong?
Can it even be done this way?
After creating a form you also need to show it. Change your logic to:
Dim form As New CustomBorderlessForm(Me)
form.Show()
Before you do that, I'd recommend changing from MsgBox to Console.WriteLine(), otherwise you can run into a fun/frustrating little cat and mouse game.
EDIT
Based on the comments, if, from VS you did a "Add New, Windows Form" you can just right-click the project, select property and on the Application tab change the Startup object to your new form. VS only allows you to do this with forms it creates for you (by default, more on this later).
If you wrote that file by hand (which is absolutely fine) you can perform the Show() like I did above and call Me.Hide() to hide the "parent" form. Unfortunately the Load event is fired before the Show event so if you place this in Form1_Load() it won't work. Instead you can use the Shown event like this:
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Dim form As New CustomBorderlessForm(Me)
form.Show()
End Sub
Private Sub Form1_Shown(sender As Object, e As EventArgs) Handles Me.Shown
Me.Hide()
End Sub
Another option has to do with "Application framework". You can read about it here however it basically handles application events that other languages have to manually implement. If you go into your project properties you can uncheck the "Enable application framework" checkbox. This will give you more option in the "Startup object" dropdown. If you add the following code to your project one of the items in the Startup object dropdown menu should now be "Loader"
Public Module Loader
<STAThread()>
Public Sub Main()
Dim form As New CustomBorderlessForm(Nothing)
form.ShowDialog()
End Sub
End Module
You'll notice that the above bypasses Form1 completely. Also, instead of Show() I'm using ShowDialog() because otherwise the form shows and then the program ends.