VB.net - Derived class from MenuStrip - vb.net

What I am trying to do is make a custom MenuStrip control with several items (Main Menu, Log Out, Exit, etc...) already attached. There would be methods to handle items being clicked. I think this would save me a some redundant code in the long run and I might learn a little something too.
The end product would basically be a custom MenuStrip control that I can throw on my forms and already have the functionality for the items within it.
So my question is, can this be done? I am a novice but if it can be done and is actually a good idea, then I want give it a shot.
Errors abound but this is what I was thinking...
Public Class MenuStripCustom
Inherits MenuStrip
Add MenuItem(MainMenuToolStripMenuItem)
MainMenuToolStripMenuItem.Text = Main Menu
Protected Sub MainMenuNav(e As System.EventArgs) _
Handles MyBase.MainMenuToolStripMenuItem.Click
MainMenu.Visible = True
Me.Close()
End Sub
End Class
Thanks!

Yes, can be done no problems. Just create a new user control, and make it inherit from MenuStrip. Then put in code similar to the below for a user control called "UserControl1".
Public Class UserControl1
Inherits MenuStrip
Private WithEvents NavToolStrip As New ToolStripMenuItem("Nav")
Sub New()
' This call is required by the designer.
InitializeComponent()
' Add any initialization after the InitializeComponent() call.
Dim tsi As New ToolStripMenuItem
Me.Items.Add(NavToolStrip)
End Sub
Private Sub NavToolStrip_Click(sender As Object, e As EventArgs) Handles NavToolStrip.Click
MsgBox("Nav clicked")
End Sub
End Class
Compile the code then you will be able to drag "UserControl1" from your toolbox onto your form.

Related

Inform programmer with a message when about to use a Usercontrol

First of all I am working with Visual Studio 2017. I have create a UserControl which requires a borderless form. Is there any way to popup a message if programmer try to place this control into a non-borderless form? Something like the message box which appears in design view when we give a wrong value at Properties Window of any control.
EDIT
I noticed that if I add a simple MsgBox into my UserControl's Public Sub New and then add this UserControl to a Form, message box appears.
Public Sub New()
InitializeComponent()
MsgBox("Test Message")
End Sub
But, how can I check if parent form is borderless or not? Something like this example below, which of course doesn't work inside Public Sub New because there is no parent yet!!!
Public Sub New()
InitializeComponent()
if Not MyBase.ParentForm.FormBorderStyle = FormBorderStyle.None Then
MsgBox("Test Message")
End If
End Sub
Well after some tests I managed to solved it...
Into UserControl's Load event (where ParentForm is acceptable), I check first if UserControl is in DesignMode (else user will gets messages everytime application starts) and then, if ParentForm is not FormBorderStyle.None, user informed by a MessageBox.
Private Sub UserControl_Load(sender As Object, e As EventArgs) Handles MyBase.Load
If Not Site Is Nothing AndAlso Site.DesignMode Then
If Not ParentForm.FormBorderStyle = FormBorderStyle.None Then
MessageBox.Show("This control works better with a borderless form!!!", "Control Info", MessageBoxButtons.OK, MessageBoxIcon.Exclamation)
End If
End If
End Sub

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

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.

How to Create MDI Child forms asynchronously?

I have Parent VB.Net form that has to create some MDI childs.
Each MDI has to connect to a database to do some staff which may take some time...
I'm trying to create the MDI child asynchronously using the following code but it stills execute synchronously :
ParentForm.BeginInvoke(Sub()
CreateMDIChildForm()
End sub)
This code executes but it's still synchronous, and the parent form freezes.
I tried to create the MDI in a separate thread but then I can't join the form created to the parent form.
Does anyone have an idea please ?
Thanks.
I can reproduce this with the following code:
Public Class Form1
Private Sub Open(sender As System.Object, e As System.EventArgs) Handles Button1.Click
Me.BeginInvoke(Sub() Me.CreateMDIChildForm())
End Sub
Private Sub CreateMDIChildForm()
Dim f As New Form2()
MsgBox("OK")
End Sub
Public Class Form2
Inherits Form
Sub New()
Thread.Sleep(5000)
End Sub
End Class
End Class
And the fix would be something like this:
Public Class Form1
Private Sub Open(sender As System.Object, e As System.EventArgs) Handles Button1.Click
If (Me.operationCompleted Is Nothing) Then
Me.operationCompleted = New SendOrPostCallback(AddressOf Me.CreateMDIChildFormCompleted)
End If
Me.asyncOperation = AsyncOperationManager.CreateOperation(Nothing)
Dim thread As New Thread(AddressOf Me.CreateMDIChildForm)
thread.Start()
End Sub
Private Sub CreateMDIChildForm()
Dim f As New Form2()
Me.asyncOperation.PostOperationCompleted(Me.operationCompleted, f)
End Sub
Private Sub CreateMDIChildFormCompleted(args As Object)
Dim f As Form = DirectCast(args, Form)
'TODO: Set mdi child, show window etc.
MsgBox("OK")
End Sub
Private asyncOperation As AsyncOperation
Private operationCompleted As SendOrPostCallback
Public Class Form2
Inherits Form
Sub New()
Thread.Sleep(5000)
End Sub
End Class
End Class
The recommended method to fix issues like this is not to try to load forms in separate threads, but, rather, to do only the time-consuming work in a separate thread.
In this circumstance, what that means is that you would load the MDI child forms normally, all on the same UI thread, but inside the child forms, they would each start a separate thread to perform the DB-related work which is causing the hangups. That way, the DB stuff won't cause the form-loading to hang up. The form will load and show itself quickly and then the database work will be done after the form is already visible.
You may need to disable some, or all, of your controls on the child form until the DB work is complete. You may also want the child form to display some sort of spinning animation to show that it is still loading. The easiest way to implement separate threads in your UI, for things like this, is to use a BackgroundWorker component. You will find it in the Components section of your form designer tool box.
Solution :
Dim Thread as new Thread(sub()
ParentForm.BeginInvoke(sub()
CreateMDIChild()
end sub)
end sub)
Thread.Start()
With this, the MDI child form is still created by the parent form but everything is done in a separate thread.