ToolStripDropDownItem - how to use correctly? vb.Net - vb.net

I am trying to figure out how to use the items I have added to my toolstrip. Below is the code I am using, I have addeda couple items "test1, test2" to the toolstrip, but how do I select them and add the code for on click events?
Thanks.
Public Class MenuStripCustom
Inherits MenuStrip
Private WithEvents NavToolStrip As New ToolStripMenuItem("File")
Sub New()
Dim tsi As New ToolStripMenuItem
Dim tsi2 As New ToolStripDropDownButton
Me.Items.Add(NavToolStrip)
NavToolStrip.DropDownItems.Add("test1")
NavToolStrip.DropDownItems.Add("test2")
End Sub
Private Sub NavToolStripDropDownButton_Click(sender As Object, e As EventArgs) Handles NavToolStrip.DropDownItemClicked
' What do I put here to handle different drop down items?
' ie. select case (dropDownItem)
' case: test1?
End Sub
End Class

there are a number of ways to add event handlers to a ToolStripMenuItem. You can pass the AddressOf it to the ToolStripMenuItem constructor or use the AddHandler keyword. However, it might be easiest for you to declare the ToolStripMenuItems "WithEvents", then you can see the events and wire them up in the VS IDE. E.g.:
Private WithEvents NavToolStrip As New ToolStripMenuItem("File")
Private WithEvents tsi As New ToolStripMenuItem("Test1")
Private WithEvents tsi2 As New ToolStripMenuItem("Test2")
Sub New()
MenuStrip1.Items.Add(NavToolStrip)
NavToolStrip.DropDownItems.Add(tsi)
NavToolStrip.DropDownItems.Add(tsi2)
End Sub
Private Sub tsi_Click(sender As Object, e As EventArgs) Handles tsi.Click
MsgBox("Test1")
End Sub
Private Sub tsi2_Click(sender As Object, e As EventArgs) Handles tsi2.Click
MsgBox("Test2")
End Sub

Related

Switching forms in vb.net

I am a volunteer for the National Park Service trying to convert an interactive display originally created 20 years ago in a language called ToolBook into Visual Basic. The program consists of several projects under a single solution. The starting project, called "MainMenu", can be thought of as a library, with buttons that bring up “books.” The project called Geology is an example “book” and GeologyMenu can be thought of as the index of a book. The buttons on GeologyMenu connect to “chapters” that explain and show examples of geologic processes in the park. The “chapters” are within the project “Geology” and work fine within the project. All forms used in the program have timers that allow the program to re-set itself to MainMenu when not in use.
In a previous post, with the help of Idle Mind (thank you again), the following code for works fine for going from MainMenu to GeologyMenu and in the reverse direction as long as no button is pushed on GeologyMenu. However, if I go to a “chapter” I can no longer get back to the MainMenu from the GeologyMenu. Here is the relevant code:
MainMenu
Public Class frmMainMenu
Private Sub BtnGeology_Click(sender As Object, e As EventArgs) Handles btnGeology.Click
Dim formNew As New Geology.frmGeologyMenu
AddHandler formNew.FormClosed, AddressOf formNew_FormClosed
TimerMain.Stop()
formNew.Show()
Me.Hide()
End Sub
Private Sub formNew_FormClosed(Sender As Object, e As FormClosedEventArgs)
lblTime.Text = 8
TimerMain.Start()
Me.Show()
End Sub
GeologyMenu
Public Class frmGeologyMenu
Public Sub frmGeologyMenu_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
lblTime.Text = 6
TimerGeologyMenu.Enabled = True
Me.Show()
End Sub
Private Sub BtnErosion_Click(sender As Object, e As EventArgs) Handles btnErosion.Click
TimerGeologyMenu.Stop()
frmErosionP01.Show()
Me.Hide()
End Sub
The code below for takes the viewer to the Erosion “chapter”
Private Sub BtnErosion_Click(sender As Object, e As EventArgs) Handles btnErosion.Click
TimerGeologyMenu.Stop()
frmErosionP01.Show()
Me.Hide()
End Sub
Erosion “Chapter” . This is the code for the button on every form in Erosion that takes the program back to GeologyMenu
Public Class frmErosionP02
Private Sub BtnGeologyMenu_Click(sender As Object, e As EventArgs) Handles btnGeologyMenu.Click
My.Computer.Audio.Stop()
frmGeologyMenu.lblTime.Text = 10
frmGeologyMenu.TimerGeologyMenu.Enabled = True
frmGeologyMenu.Show()
Me.Close()
End Sub
The code for forms within Erosion takes me back to GeologyMenu, but then MainMenu won’t show when I close GeologyMenu and I don’t understand why or how to fix it. Thank you in advance for your help!
Simply, pass the previous menu/Form to the new one in a parameterized constructor and keep it in a class variable, then handle the Form.Closed event of the new menu to show the previous one.
Example for the relevant code:
Public Class frmMainMenu
Inherits Form
Private Sub BtnGeology_Click(sender As Object, e As EventArgs) Handles btnGeology.Click
Dim formNew As New frmGeologyMenu(Me)
Me.Hide()
formNew.Show()
End Sub
End Class
Public Class frmGeologyMenu
Inherits Form
Private PreviousMenu As Form
Private Sub New()
InitializeComponent()
'...
End Sub
Sub New(prevMenu As Form)
Me.New()
PreviousMenu = prevMenu
AddHandler FormClosed,
Sub(s, e)
PreviousMenu.Show()
End Sub
End Sub
Private Sub BtnErosion_Click(sender As Object, e As EventArgs) Handles btnErosion.Click
Dim frmErosion As New frmErosionP02(Me)
Me.Hide()
frmErosion.Show()
End Sub
End Class
Public Class frmErosionP02
Inherits Form
Private PreviousMenu As Form
Private Sub New()
InitializeComponent()
'...
End Sub
Public Sub New(prevMenu As Form)
Me.New()
PreviousMenu = prevMenu
AddHandler FormClosed,
Sub(s, e)
PreviousMenu.Show()
End Sub
End Sub
End Class

ContextMenuStrip Requires Two Right Clicks to Display

I like to create my contextmenu's programmatically. I generally do not add the items to the contextmenustrip until it is opening as the items that get displayed are dependent on other aspects of the design that are variable.
I have found that the contextmenustrips seem to require two right clicks to display. I've tried adding the menu items in different events (opening, opened, etc) and also manually setting the contextmenustrip's visibility to true to no avail.
I can't for the life of me figure out why two right clicks are necessary. If you create a blank winforms project and then replace all the code with this, it'll demonstrate the issue.
Public Class Form1
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles Me.Load
Dim currContextMenuStrip As New ContextMenuStrip
Me.ContextMenuStrip = currContextMenuStrip
AddHandler currContextMenuStrip.Opening, AddressOf ContextMenuStrip1_Opening
End Sub
Private Sub ContextMenuStrip1_Opening(sender As Object, e As CancelEventArgs)
Dim currContextMenuStrip As ContextMenuStrip = sender
Dim menuTxt As String = "&Find"
'only add the menu if it doesn't already exist
If (From f In currContextMenuStrip.Items Where f.text = menuTxt).Count = 0 Then
Dim newMenuItem As New ToolStripMenuItem
newMenuItem.Text = menuTxt
currContextMenuStrip.Items.Add(newMenuItem)
End If
End Sub
End Class
EDIT: Just figured out it seems to be connected to the fact that the contextmenustrip doesn't have any items on the first right click. If I add a dummy item, then hide it once other items are added, it works on the first right click. So confused!
This works:
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles Me.Load
Dim currContextMenuStrip As New ContextMenuStrip
Me.ContextMenuStrip = currContextMenuStrip
AddHandler currContextMenuStrip.Opening, AddressOf ContextMenuStrip1_Opening
'add a dummy item
Dim newMenuItem As New ToolStripMenuItem
newMenuItem.Text = "dummy"
currContextMenuStrip.Items.Add(newMenuItem)
End Sub
Private Sub ContextMenuStrip1_Opening(sender As Object, e As CancelEventArgs)
Dim currContextMenuStrip As ContextMenuStrip = sender
Dim menuTxt As String = "&Find"
'only add the menu if it doesn't already exist
If (From f In currContextMenuStrip.Items Where f.text = menuTxt).Count = 0 Then
Dim newMenuItem As New ToolStripMenuItem
newMenuItem.Text = menuTxt
currContextMenuStrip.Items.Add(newMenuItem)
End If
'hide the dummy item
Dim items As List(Of ToolStripMenuItem) = (From f As ToolStripMenuItem In currContextMenuStrip.Items Where f.Text = "dummy").ToList
items.First.visible = False
End Sub
End Class
If you really need to do things this way, one option is to create your own custom ContextMenuStrip that accounts for the behaviour when there are no items and the requirement for a dummy item. I used this code:
Imports System.ComponentModel
Public Class Form1
Private WithEvents menu As New ContextMenuStrip
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
ContextMenuStrip = menu
End Sub
Private Sub menu_Opening(sender As Object, e As CancelEventArgs) Handles menu.Opening
If menu.Items.Count = 0 Then
menu.Items.AddRange({New ToolStripMenuItem("First"),
New ToolStripMenuItem("Second"),
New ToolStripMenuItem("Third")})
End If
End Sub
Private Sub menu_ItemClicked(sender As Object, e As ToolStripItemClickedEventArgs) Handles menu.ItemClicked
MessageBox.Show(e.ClickedItem.Text)
End Sub
End Class
and saw the same behaviour you describe. I then defined this class:
Public Class ContextMenuStripEx
Inherits ContextMenuStrip
Private dummyItem As ToolStripItem
Public ReadOnly Property IsInitialised As Boolean
Get
Return dummyItem Is Nothing
End Get
End Property
Public Sub New()
dummyItem = Items.Add(CStr(Nothing))
End Sub
''' <inheritdoc />
Protected Overrides Sub OnItemAdded(e As ToolStripItemEventArgs)
If Not IsInitialised Then
Items.Remove(dummyItem)
dummyItem = Nothing
End If
MyBase.OnItemAdded(e)
End Sub
End Class
and changed my form code to this:
Imports System.ComponentModel
Public Class Form1
Private WithEvents menu As New ContextMenuStripEx
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
ContextMenuStrip = menu
End Sub
Private Sub menu_Opening(sender As Object, e As CancelEventArgs) Handles menu.Opening
If Not menu.IsInitialised Then
menu.Items.AddRange({New ToolStripMenuItem("First"),
New ToolStripMenuItem("Second"),
New ToolStripMenuItem("Third")})
End If
End Sub
Private Sub menu_ItemClicked(sender As Object, e As ToolStripItemClickedEventArgs) Handles menu.ItemClicked
MessageBox.Show(e.ClickedItem.Text)
End Sub
End Class
and it worked as desired. Note that the last code snippet uses the custom type and its custom property.
Thanks for all the help and suggestions! I ultimately decided to build the superset of menus in the Designer and then just show/hide at run time. That's probably faster on each right click then rebuilding the menu each time.
Microsoft has old style and new style context menus. The Popup event was used for the old style context menus and it received a plain EventArgs object. The new context menus use the Opening event which receives a CancelEventArgs object. If currContextMenuStrip.Items doesn't contain any items, e.Cancel will be set to True when the event handler is called (which caused the problem you encountered). The fix is to add the menu items and then set e.Cancel to False. It should display fine after that. To make sure items were actually added, the assignment of e.Cancel can be guarded with an if statement as follows:
If currContextMenuStrip.Items.Count <> 0 Then
e.Cancel = False
End If

Run some code when any control on a list loses focus

Currently, I have this, on my Form.vb:
Private Sub txtBox1_Leave(sender As Control, e As EventArgs) Handles txtBox1.Leave
'Some code
End Sub
...
Private Sub txtBox10_Leave(sender As Control, e As EventArgs) Handles txtBox10.Leave
'Some code
End Sub
The thing that bothers me is: All those events are doing the same thing. Is it possible to programatically have a list of the relevant controls and iterate through them, adding such events? This would allow me to reduce the amount of code in my application / coding effort. Something like:
For Each c As Control in listOfControls
'Add event for c here which calls method
Next
I really think there is a simple way of doing that but everything I've tried so far (such as AddHandler) did not work. Any ideas?
Thank you
Yes it is possible and quite simple, create a method that will add the requested event to the Control, pass an array of controls to that method before the form loads:
Public Class Form1
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
' add events to all requested controls
AddEvent(New Control() {TextBox1, TextBox2, TextBox3, Button1})
End Sub
Public Sub AddEvent(ByVal myControls() As Control)
For Each c As Control In myControls
AddHandler c.Leave, AddressOf Control_Leave
Next
End Sub
Private Sub Control_Leave(sender As Object, e As EventArgs)
MsgBox("Control is not in focus")
End Sub
End Class

Changing Controls of a form inside a panel

I am facing a weird problem
I have 3 forms: MainForm, Form1, Form2
MainForm has 1 Panel: Panel1
Form1 has 1 Label: NameLbl and Button: ChangeBtn
Form2 has 1 textbox: NameTxt and Button: SaveBtn
I used the following code to open form1 inside Panel1 in mainform
Panel1.Controls.Clear()
Dim FormInPanel As New Form1()
FormInPanel.TopLevel = False
Panel1.Controls.Add(FormInPanel)
FormInPanel.Show()
On ChangeBtn.Click Form2 opens as showdialog
I want NameLbl.text to change to NameLbl.text when SaveBtn is clicked But normal code doesnt work.
Private Sub SaveBtn_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles SaveBtn.Click
Form1.NameLbl.text=NameTxt.text
End Sub
What Should I do? Any suggestions? Given that i need to open the forms in panels for certain reasons.
Please keep in mind that this is just an example. I have multiple controls in Form1 which i want to change on form2.SaveBtn.click
I have also tried this but it does nothing
Private Sub SaveBtn_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles SaveBtn.Click
For Each c As Control In MainForm.Panel1.Controls(0).Controls
If c.Name="NameLbl" Then
c.Text = NameTxt.Text
End If
Next
End Sub
Please Somebody tell me how it do it!
Form2 doesn't seem to have any connection back to Form1 or MainForm. You'd need to raise an event from Form2 which MainForm handles or another class handles and can pass to MainForm
Edit:
Sorry, I've just seen how you're calling Form2. There are lots of ways to get a value back from Form2 after calling ShowDialog(). One is to create a property called Result and check Result if ShowDialog() == DialogResult.OK. Something like the following.
Public Class Form2
Inherits System.Windows.Forms.Form
Public Property Result() As String
Get
Return m_Result
End Get
Set
m_Result = Value
End Set
End Property
Private m_Result As String
End Class
Public Class Form1
Inherits System.Windows.Forms.Form
Public ChangeBtn As Button
Public NameLbl As Label
Public Sub New()
Me.ChangeBtn = New Button()
AddHandler Me.ChangeBtn.Click, AddressOf ChangeBtn_Click
Me.NameLbl = New Label()
End Sub
Private Sub ChangeBtn_Click(sender As Object, e As EventArgs)
Dim form As New Form2()
Dim dr = New form.ShowDialog()
If dr = DialogResult.OK Then
Me.NameLbl.Text = form.Result
End If
End Sub
End Class
I'd like to add that if you plan on growing this application much larger you'll run into issues with maintenance. Look into some patterns for dealing with UI logic like MVC, MVP, MVVM if you're interested.
you can try this code:
Private Sub SaveBtn_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles SaveBtn.Click
panel1.Controls(0).NameLbl.text=NameTxt.text '"0" is the index of forminpanel in panel1,maybe it need to change.
End Sub
Form1 is contained in Panel1, so you can not access it via
Form1.
Only MainForm is visible from Form2:
Private Sub SaveBtn_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles SaveBtn.Click
For Each c As Control In MainForm.Panel1.Controls(0).Controls
If TypeOf c Is TextBox Then
c.Text = NameTxt.Text
End If
Next
End Sub
Try this:
For Each form1 As Form1 In MainForm.OwnedForms.OfType(Of Form1)
Form1.NameLbl.text = NameTxt.text
Next
I've faced same issue and I've fixed with this
Dim f As FormInPanel
f = Form.Panel1.Controls(0)
f.transection = True
f.NameLbl.text=NameTxt.text

VB.NET UserControl does not receive parent form's events

I have created a usercontrol and added it to a form.
I would like to receive the form's event using Private WithEvents _Parent As Form.
But none of the events is being received.
The entire code of my usercontrol is attached.
Does anybody see what I am doing wrong?
Public Class UserControl1
Private WithEvents _Parent As Form
Public Sub New()
InitializeComponent()
_Parent = Me.Parent
End Sub
Private Sub _Parent_Activated(sender As Object, e As EventArgs) Handles _Parent.Activated
MsgBox("activated")
End Sub
Private Sub _Parent_Resize(sender As Object, e As EventArgs) Handles _Parent.Resize
MsgBox("resize")
End Sub
End Class
When the constructor is called, there is no parent yet (Windows Forms controls are added to their parents after the class is created). Me.Parent returns Nothing at this point.
Handle Me.ParentChanged to initialize _Parent:
Private Sub UserControl1_ParentChanged(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.ParentChanged
_Parent = Me.Parent
End Sub