I have 3 Forms namely MainForm, Form1 and Form2. MainForm hosts Form1 in a Panel. On Clicking a button in MainForm, I am opening Form2 using ShowDialog() method. Now I have a treeview in Form2. Now I want to pass the nodes selected in Form2 back to a combobox in Form1. How can this be achieved? I have tried Form1.Activate() in Form2 but the code is not hitting Activate method in Form1.
Also I am using Form1.ComboBox1.Items.Add(Me.TreeView1.SelectedNode.Text) but I cannot see any items in ComboBox once Form2 is closed. What am I missing here?
Below is the code for better understanding.
MainForm
public Class MainForm
private Sub OpenChildForm(childForm As Form)
panelFormContainer.Controls.Add(childForm)
childForm.Dock = DockStyle.Fill
childForm.Show()
End Sub
private sub MainForm_OnLoad(sender As Object, e as EventArgs) Handles Me.Load
'Adding child form to a Panel in Main Form
OpenChildForm(new Form1())
End Sub
'Open Form 2 on Button Click
private sub btnOpenForm3_Click(sender As Object, e as EventArgs) Handles btnOpenForm3.Click
Form2.ShowDialog()
End Sub
End Class
Form2 - Child Form Opened by button click in MainForm
Public Class Form2
'Click back button to go back to Main Form which is already having Form1 as child
Private Sub btnBack_Click(sender As Object, e As EventArgs)
Me.Close()
Form1.Activate()
End Sub
'Click a Button to Add Selected Treeview node to Combo in Form1
Private Sub btnAdd_Click(sender As Object, e As EventArgs) btnAdd.Click
Form1.ComboBox1.Items.Add(Me.TreeView1.SelectedNode.Text)
End Sub
End Class
Updated: I have updated the code but still not getting anything in ComboBox of Child Form1
MainForm
Public Class Form1
Private currentChildForm As Form = Nothing
Private ownerForm As Form = Nothing
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
OpenChildForm(New ChildForm1())
End Sub
Private Sub OpenChildForm(childForm As Form)
If currentChildForm IsNot Nothing Then
currentChildForm.Close()
End If
childForm.TopLevel = False
childForm.FormBorderStyle = FormBorderStyle.None
panelFormContainer.Controls.Clear()
panelFormContainer.Controls.Add(childForm)
childForm.Dock = DockStyle.Fill
childForm.BringToFront()
childForm.Show()
End Sub
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Dim dialogForm As ChildForm2 = New ChildForm2()
'
Dim result = dialogForm.ShowDialog()
If result = DialogResult.OK Then
AddHandler ChildForm2.Button1.Click, AddressOf ChildForm1.objForm2_Passvalue
End If
End Sub
End Class
ChildForm1 which is hosted in MainForm
Public Class ChildForm1
Private Sub ChildForm1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
If ComboBox1.Items.Count > 0 Then
ComboBox1.SelectedIndex = 0
End If
End Sub
Private Sub ChildForm1_Activated(sender As Object, e As EventArgs) Handles Me.Activated
End Sub
Public Sub objForm2_Passvalue(sender As Object, e As EventArgs)
Me.ComboBox1.Items.Add(PageDetail.PageTitle)
End Sub
End Class
ChildForm2 -- Which is opened as Dialog
Public Class ChildForm2
Private Sub ChildForm2_Load(sender As Object, e As EventArgs) Handles MyBase.Load
TreeView1.ExpandAll()
Button1.DialogResult = DialogResult.OK
End Sub
Public Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
PageDetail.PageTitle = TreeView1.SelectedNode.Text
Me.Close()
End Sub
Private Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click
Me.Close()
End Sub
End Class
PageDetail: Class used to get and set data
Public NotInheritable Class PageDetail
Private Shared pageTitleValue As String
Public Shared Property PageTitle As String
Get
Return pageTitleValue
End Get
Set(value As String)
pageTitleValue = value
End Set
End Property
End Class
The following shows multiple ways to pass data between forms. In the code below, I show how to retrieve data from a child form (ChildForm2) using a function, a property, or an event. This data is passed back to the parent form (MainForm). Once the parent form (MainForm) receives the data, it sends the data to a different child form (ChildForm1). Data can be sent from the parent form (MainForm) to the child form (ChildForm1) using one of the constructors, a method, or a property.
Note: MainForm is the startup form and is the parent to both ChildForm1 and ChildForm2 (ie: instances of both ChildForm1 and ChildForm2 are created in MainForm)
Create a new project
VS 2019:
In VS menu, click File
Select New
Select Project
Select Windows Forms App (.NET Framework)
Click Next
Enter desired project name
Click Create
Open Solution Explorer
In VS menu, select View
Select Solution Explorer
Add Form (Name: ChildForm1.vb)
In Solution Explorer, right-click <project name>, select Add
Select Form (Windows Forms)... (name: ChildForm1.vb)
Add a Label (Text: "Select One")
Add a ComboBox (Name: ComboBox1)
In Solution Explorer, right click ChildForm1.vb, and select View Code
ChildForm1.vb
Public Class ChildForm1
Public WriteOnly Property PageTitle As String
Set(value As String)
'populate ComboBox
PopulateComboBox(value)
End Set
End Property
Sub New()
' This call is required by the designer.
InitializeComponent()
' Add any initialization after the InitializeComponent() call.
End Sub
Sub New(pageTitle As String)
' This call is required by the designer.
InitializeComponent()
' Add any initialization after the InitializeComponent() call.
PopulateComboBox(pageTitle)
End Sub
Sub New(pageTitles As List(Of String))
' This call is required by the designer.
InitializeComponent()
' Add any initialization after the InitializeComponent() call.
PopulateComboBox(pageTitles)
End Sub
Private Sub ChildForm1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
End Sub
Public Sub PopulateComboBox(pageTitle As String)
'create new instance
Dim pageTitles As New List(Of String)
'add
pageTitles.Add(pageTitle)
'populate ComboBox
PopulateComboBox(pageTitles)
End Sub
Public Sub PopulateComboBox(pageTitles As List(Of String))
'remove existing data from ComboBox
ComboBox1.Items.Clear()
ComboBox1.Text = String.Empty
For Each pTitle In pageTitles
'add
ComboBox1.Items.Add(pTitle)
Next
If ComboBox1.Items.Count = 1 Then
'if only 1 item exists, select it
ComboBox1.SelectedIndex = 0
End If
End Sub
End Class
Add a Form (name: ChildForm2.vb)
In Solution Explorer, right-click <project name>, select Add
Select Form (Windows Forms)... (Name: ChildForm2.vb)
Add a Label
Add a TreeView (name: TreeView1)
Add a Button (Name: btnAdd; Text: Add)
Double-click "btnAdd" to add the Click event handler
Add a Button (Name: btnCancel; Text: Cancel)
Double-click "btnCancel" to add the Click event handler
In Solution Explorer, right click ChildForm2.vb, and select View Code
ChildForm2.vb
Public Delegate Sub PassValueHandler(ByVal strValue As String)
Public Class ChildForm2
Public Event PassValue As PassValueHandler
Public ReadOnly Property PageTitle
Get
Return TreeView1.SelectedNode.Text
End Get
End Property
Private Sub Form2_Load(sender As Object, e As EventArgs) Handles MyBase.Load
PopulateTreeView()
TreeView1.ExpandAll()
End Sub
Public Function GetPageTitle() As String
Return PageTitle
End Function
Private Sub PopulateTreeView()
'ToDo: Replace this method with code to populate your TreeView
TreeView1.Nodes.Clear()
'Parent 1
Dim node1 As TreeNode = New TreeNode("Parent 1")
Dim childNode1 As TreeNode = New TreeNode("Child Node 1")
'add
node1.Nodes.Add(childNode1)
'add
TreeView1.Nodes.Add(node1)
'Parent 2
Dim node2 As TreeNode = New TreeNode("Parent 2")
Dim childNode2 As TreeNode = New TreeNode("Child Node 2")
'add
node2.Nodes.Add(childNode2)
'add
TreeView1.Nodes.Add(node2)
End Sub
Private Sub btnAdd_Click(sender As Object, e As EventArgs) Handles btnAdd.Click
'raise event
RaiseEvent PassValue(TreeView1.SelectedNode.Text)
'set value
Me.DialogResult = DialogResult.OK
'close
Me.Close()
End Sub
Private Sub btnCancel_Click(sender As Object, e As EventArgs) Handles btnCancel.Click
'set value
Me.DialogResult = DialogResult.Cancel
'close
Me.Close()
End Sub
End Class
Rename Form1 to MainForm
In Solution Explorer, right-click Form1.vb
Select Rename
Enter MainForm.vb
When prompted "You are renaming a file. Would you also like to perform a rename in this project of all references to the code element 'Form1'? Click Yes
In Properties Window, for "MainForm", set Text = "MainForm"
MainForm
Add a panel to MainForm (Name: panelFormContainer)
Add Button (Name: btnOpenChildForm2; Text: Open ChildForm2)
Double-click "btnOpenChildForm2" to add the Click event handler
In Solution Explorer, right click MainForm.vb, and select View Code
In the code below, I've written it in such a way that it allows one to choose different options for both retrieving data from a child form, as well as, multiple options for sending data to a child form.
MainForm.vb
Public Class MainForm
Private dialogForm As ChildForm2 = Nothing
Private currentChildForm As Form = Nothing
Private ownerForm As Form = Nothing
Private Sub MainForm_Load(sender As Object, e As EventArgs) Handles MyBase.Load
'open Form
OpenChildForm(New ChildForm1())
End Sub
Private Sub OpenChildForm(ByRef childForm As Form)
If currentChildForm IsNot Nothing Then
currentChildForm.Dispose()
currentChildForm = Nothing
End If
'set value
currentChildForm = childForm
'set properties
childForm.Dock = DockStyle.Fill
childForm.TopLevel = False
childForm.FormBorderStyle = FormBorderStyle.None
'remove existing controls
panelFormContainer.Controls.Clear()
'add
panelFormContainer.Controls.Add(childForm)
'show
childForm.Show()
End Sub
Private Sub PopulateChildForm1ComboBox(pageTitle As String)
If currentChildForm.GetType() = ChildForm1.GetType() Then
'currentChildForm is an instance of ChildForm1
'create reference
Dim frm = CType(currentChildForm, ChildForm1)
'option 1 - populate ComboBox by calling method
frm.PopulateComboBox(pageTitle)
'option 2 - populate ComboBox by setting property
'frm.PageTitle = PageDetail.PageTitle
End If
End Sub
Private Sub btnOpenChildForm2_Click(sender As Object, e As EventArgs) Handles btnOpenChildForm2.Click
'ToDo: Replace this method with code from Option 1, Option 2, or Option 3 below
...
End Sub
Private Sub DialogForm_BtnAdd_Click(sender As Object, e As System.EventArgs)
'option 1 - get page title from property
PageDetail.PageTitle = dialogForm.PageTitle
'option 2 - get page title by calling function
'PageDetail.PageTitle = dialogForm.GetPageTitle()
'populate ComboBox
PopulateChildForm1ComboBox(PageDetail.PageTitle)
End Sub
Private Sub DialogForm_PassValue(e As String)
'set value
PageDetail.PageTitle = e
'populate ComboBox
PopulateChildForm1ComboBox(e)
End Sub
End Class
Choose one of the following options for retrieving data from ChildForm2. Replace the method btnOpenChildForm2_Click (in MainForm.vb) with the code listed below.
Option 1 (DialogResult.OK)
Private Sub btnOpenChildForm2_Click(sender As Object, e As EventArgs) Handles btnOpenChildForm2.Click
'create new instance
dialogForm = New ChildForm2()
'show dialog
If dialogForm.ShowDialog() = DialogResult.OK Then
PageDetail.PageTitle = dialogForm.PageTitle
'populate ComboBox
PopulateChildForm1ComboBox(PageDetail.PageTitle)
End If
'dispose
dialogForm.Dispose()
dialogForm = Nothing
End Sub
Note: When using Option 1, the code for DialogForm_BtnAdd_Click and DialogForm_PassValue (in MainForm.vb) isn't used, so both of these methods can be removed.
Option 2 (subscribe to btnAdd 'Click' event)
Private Sub btnOpenChildForm2_Click(sender As Object, e As EventArgs) Handles btnOpenChildForm2.Click
'create new instance
dialogForm = New ChildForm2()
'subscribe to events (add event handlers)
AddHandler dialogForm.btnAdd.Click, AddressOf DialogForm_BtnAdd_Click
'show dialog
dialogForm.ShowDialog()
'unsubscribe from events (remove event handlers)
RemoveHandler dialogForm.btnAdd.Click, AddressOf DialogForm_BtnAdd_Click
'dispose
dialogForm.Dispose()
dialogForm = Nothing
End Sub
Note: When using Option 2, the code for DialogForm_PassValue (in MainForm.vb) isn't used, so method DialogForm_PassValue can be removed.
Option 3 (subscribe to event 'PassValue')
Private Sub btnOpenChildForm2_Click(sender As Object, e As EventArgs) Handles btnOpenChildForm2.Click
'create new instance
dialogForm = New ChildForm2()
'subscribe to events (add event handlers)
AddHandler dialogForm.PassValue, AddressOf DialogForm_PassValue
'show dialog
dialogForm.ShowDialog()
'unsubscribe from events (remove event handlers)
RemoveHandler dialogForm.PassValue, AddressOf DialogForm_PassValue
'dispose
dialogForm.Dispose()
dialogForm = Nothing
End Sub
Note: When using Option 3, the code for DialogForm_BtnAdd_Click (in MainForm.vb) isn't used, so method DialogForm_BtnAdd_Click can be removed.
Here's a demo:
Resources:
Form.ShowDialog
Form.Show
How to populate a treeview from a list of objects
Rather than placing a Form on a panel, you might consider creating a UserControl and placing that on the panel. The following show how to pass data from a Form (Form2) that is shown using ShowDialog() to a UserControl (UserControl1) that exists on a different form (MainForm).
Note: MainForm is the startup form. If desired, the UserControl can be replaced with a Form.
Create a new project
VS 2019:
In VS menu, click File
Select New
Select Project
Select Windows Forms App (.NET Framework)
Click Next
Enter desired project name
Click Create
Open Solution Explorer
In VS menu, select View
Select Solution Explorer
Add UserControl (Name: UserControl1.vb)
In Solution Explorer, right-click <project name>, select Add
Select User Control (Windows Forms)... (name: UserControl1.vb)
Add a Label (Text: "Select One")
Add a ComboBox (Name: ComboBox1)
UserControl1.vb
Public Class UserControl1
Private Sub UserControl1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
End Sub
Public Sub PopulateComboBox(value As String)
'remove existing data from ComboBox
ComboBox1.Items.Clear()
ComboBox1.Text = String.Empty
'add
ComboBox1.Items.Add(value)
If ComboBox1.Items.Count = 1 Then
'if only 1 item exists, select it
ComboBox1.SelectedIndex = 0
End If
End Sub
End Class
Note: If using a Form instead of a UserControl, add the code for PopulateComboBox to your form.
Add a Form (name: Form2.vb)
In Solution Explorer, right-click <project name>, select Add
Select Form (Windows Forms)... (Name: Form2.vb)
Add a Label
Add a TreeView (name: TreeView1)
Add a Button (Name: btnAdd; Text: Add)
Double-click "btnAdd" to add the Click event handler
Add a Button (Name: btnCancel; Text: Cancel)
Double-click "btnCancel" to add the Click event handler
In Solution Explorer, right click Form2.vb, and select View Code
Form2.vb
Imports System.IO
Public Class Form2
Private Sub Form2_Load(sender As Object, e As EventArgs) Handles MyBase.Load
PopulateTreeView()
End Sub
'ToDo: Replace this function with one that returns the desired data
Public Function GetSelectedValue() As String
Return TreeView1.SelectedNode.Text
End Function
Private Sub PopulateTreeView()
'ToDo: Replace this code with your desired code to populate the TreeView
'clear
TreeView1.Nodes.Clear()
Dim topNode As TreeNode = New TreeNode("Computer")
TreeView1.Nodes.Add(topNode)
Dim logicalDrives As String() = Directory.GetLogicalDrives()
If logicalDrives IsNot Nothing Then
For Each drive As String In logicalDrives
Debug.WriteLine("drive: " & drive.ToString())
Try
Dim dirInfo As DirectoryInfo = New DirectoryInfo(drive)
TreeView1.Nodes.Add(New TreeNode(drive))
Catch ex As Exception
'do nothing
End Try
Next
End If
End Sub
Private Sub btnAdd_Click(sender As Object, e As EventArgs) Handles btnAdd.Click
'in MainForm we'll subscribe to the Add button Click event and retrieve the data by calling function "GetSelectedValue", so all we have to do here is close the form
Me.Close()
End Sub
Private Sub btnCancel_Click(sender As Object, e As EventArgs) Handles btnCancel.Click
Me.Close()
End Sub
End Class
Rename Form1 to MainForm
In Solution Explorer, right-click Form1.vb
Select Rename
Enter MainForm.vb
When prompted "You are renaming a file. Would you also like to perform a rename in this project of all references to the code element 'Form1'? Click Yes
In Properties Window, for "MainForm", set Text = "MainForm"
Build Project
In Solution Explorer, right-click <project name>, select Build
MainForm
Add a panel to MainForm (Name: panel1)
In Toolbox (View => Toolbox), expand: <solution name> Components
Drag UserControl1 onto panel1 on MainForm
Add Button (Name: btnOpenForm2; Text: Open Form2)
Double-click "btnOpenForm2" to add the Click event handler
In Solution Explorer, right click MainForm.vb, and select View Code
MainForm.vb
Public Class MainForm
Private frm2 As Form2 = Nothing
Private Sub MainForm_Load(sender As Object, e As EventArgs) Handles MyBase.Load
End Sub
Private Sub btnOpenForm2_Click(sender As Object, e As EventArgs) Handles btnOpenForm2.Click
If frm2 Is Nothing Then
'create new instance
frm2 = New Form2()
End If
'subscribe to events (add event handlers)
AddHandler frm2.btnAdd.Click, AddressOf Frm2BtnAdd_Click
'show dialog
frm2.ShowDialog()
'the code below will execute after frm2 is closed
'unsubscribe from events (remove event handlers)
RemoveHandler frm2.btnAdd.Click, AddressOf Frm2BtnAdd_Click
'dispose
frm2.Dispose()
frm2 = Nothing
End Sub
Private Sub Frm2BtnAdd_Click(sender As Object, e As System.EventArgs)
'call method to populate ComboBox
'UserControl11.PopulateComboBox(frm2.GetSelectedValue())
'call function to get data
Dim userSelection As String = frm2.GetSelectedValue()
'call method to populate ComboBox
UserControl11.PopulateComboBox(userSelection)
End Sub
End Class
Here's a demonstration:
Resources
How to populate a treeview from a list of objects
Related
I have three forms in total and i want to trigger one of the button on form 3 to be triggered automatically when form 1 loaded
Form 1
Public Class frmIOMain
' In This Form load I want to trigger the above mentioned button
Private Sub IOMain_Load(sender As Object, e As System.EventArgs) Handles Me.Load
' I want to Trigger the Above mentioned button here when my form is loaded
' But it is not working for me
frmUpdateDueDates.cmdUpdate_Click(Nothing, Nothing)
End Sub
End Class
Form 2
Public Class TestEquipmentManagement
Public EquipementTable As New DataTable("EquipmentTable")
Public EquiTypeSelection As String
Public EquiManufacturerSelection As String
Public EquiList_PK As New List(Of Integer)
Dim targetEquipmentList As New List(Of Model.equipment)
Private equipDB As Model.Entities = Nothing
Public Shared viewManager As ViewManager
Private equipment As New List(Of Model.equipment)
'Dim WithEvents excNewPFM As New VBACom
Public EquipCalTable As New DataTable("EquipCalTable")
Public Sub New()
Dim todayplusoneyear As Date
todayplusoneyear = Date.Today
todayplusoneyear = todayplusoneyear.AddYears(1)
'Assign current db
equipDB = frmIOMain.db
End Sub
End Class
Form 3
Public Class frmUpdateDueDates
Private EquipmentUpdates As UpdateCalibrationsViewModel
Private _success As Boolean = False
Public Sub New(db As Entities)
' Dieser Aufruf ist für den Designer erforderlich.
InitializeComponent()
EquipmentUpdates = New UpdateCalibrationsViewModel(db, New CAQ23(), False)
'Add Handlers
AddHandler EquipmentUpdates.OnProgressChanged, AddressOf progressChangedHandler
AddHandler EquipmentUpdates.OnInfotextChanged, AddressOf infoTextChangedHandler
prgUpdates.Maximum = EquipmentUpdates.intProgressMax
End Sub
Public Sub cmdUpdate_Click(sender As Object, e As EventArgs) Handles cmdUpdate.Click
cmdUpdate.Enabled = False
_success = EquipmentUpdates.startUpdating()
cmdCancel.Text = "Close"
End Sub
End Class
I want "cmdUpdate_Click" Button which is on form 3 to be triggered when my form 1 is loaded
Can Anyone tell me how i can do that?
Firstly, create an instance of the form, instead of using its default form instance. Calling a click handler across forms isn't a good idea. The handler may use the arguments sender As Object, e As EventArgs and from outside of the containing class, you can't assume you know that. Better practice would be to create a method which performs the click within the form, such as
Public Class frmUpdateDueDates
Public Sub cmdUpdateClick()
cmdUpdate.PerformClick()
End Sub
Private Sub cmdUpdate_Click(sender As Object, e As EventArgs) Handles cmdUpdate.Click
cmdUpdate.Enabled = False
_success = EquipmentUpdates.startUpdating()
cmdCancel.Text = "Close"
End Sub
End Class
Public Class frmIOMain
Private myFrmUpdateDueDates As frmUpdateDueDates
Private Sub IOMain_Load(sender As Object, e As System.EventArgs) Handles Me.Load
myFrmUpdateDueDates = New FrmUpdateDueDates()
myFrmUpdateDueDates.Show()
'myFrmUpdateDueDates.cmdUpdate_Click(Nothing, Nothing)
myFrmUpdateDueDates.cmdUpdateClick()
End Sub
End Class
And you can change the access modifier of the click handler back to Private
Even better would be to put the work into a different method which the click handler calls. Then the other form doesn't even need to know the button exists. Such as
Public Class frmUpdateDueDates
Public Sub DoUpdating()
cmdUpdate.Enabled = False
_success = EquipmentUpdates.startUpdating()
cmdCancel.Text = "Close"
End Sub
Private Sub cmdUpdate_Click(sender As Object, e As EventArgs) Handles cmdUpdate.Click
DoUpdating()
End Sub
End Class
Public Class frmIOMain
Private myFrmUpdateDueDates As frmUpdateDueDates
Private Sub IOMain_Load(sender As Object, e As System.EventArgs) Handles Me.Load
myFrmUpdateDueDates = New FrmUpdateDueDates()
myFrmUpdateDueDates.Show()
'myFrmUpdateDueDates.cmdUpdate_Click(Nothing, Nothing)
myFrmUpdateDueDates.DoUpdating()
End Sub
End Class
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
This is pretty rudimentary, but I can't figure it out.
I want to programmatically close form1 when form2 closes, if form1 is still open. Form2 was opened by a command button on form1.
The code on Form1 that opens Form2 is:
Dim frm As New form2
frm.Show()
What's the best way when Form2 closes to close any open copies of Form1 that are open, also?
If you want to handle your two forms independently, you need to watch over them from a third form or class. So my suggestion would be to create both of them in this third class, and pass a reference of the second form to the first form so it can then open it. This way:
Public Class MyHelper
Public Sub CreateForms()
Dim form2 as New Form2()
AddHandler form2.Closed, AddressOf Form2_OnClosed
‘ Create as many copies as you need
Dim form1 as New Form1(form2)
form1.Show()
End Sub
Protected Sub Form2_OnClosed(sender as object, e as EventArgs)
‘ Same code for each form1 that has been created and opened.
If (form1.IsOpen) Then form1.Close()
End Sub
End Class
Public Class Form1
Private _form2 as Form2
Public Property IsOpen as Boolean = false
Public Sub New(form2 as Form2)
_form2 = form2
End Sub
Protected Sub MyButton_Click(sender as object, e as EventArgs) handles MyButton.Click
‘ You open your form here or wherever you want (even on the constructor)
_form2.Show()
End Sub
Protected Sub Me_OnClosed(sender as object, e as EventArgs) handles Me.Closed
Me.IsOpen = false
End Sub
Protected Sub Me_OnShown(sender as object, e as EventArgs) handles Me.Shown
Me.IsOpen = true
End Sub
End Class
Add this reference to make it work.
Imports System.Linq
If Application.OpenForms().OfType(Of Form1).Any Then
MsgBox("Form1 is open")
End If
Supposing you have 3 forms and want to close the other two on button click
Private Sub EMPLEADOToolStripMenuItem_Click(sender As Object, e As EventArgs) Handles EMPLEADOToolStripMenuItem.Click
If Application.OpenForms().OfType(Of BUSCAR_INDEX).Any Then
BUSCAR_INDEX.Close()
ElseIf Application.OpenForms().OfType(Of MIEMBROS_INDEX).Any Then
MIEMBROS_INDEX.Close()
End If
EMP_INDEX.Show()
EMP_INDEX.EmpIDTextBox.Text = EmpIDTextBox.Text
End Sub
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
So basically you have a main form that shows only a label (lblTotalPrice) and a button that when you click it, it opens a second form that contains a listbox with different prices packages (lstPackages). How do do you select an item from lstPackages that is on the second form and return it to lblTotalPrice, which is located on the main form?
I attempted this code using a function (thought it would be useful for calculations), but it seems like it didn't work:
On my second form, I populated the lstPackages with it's name using parallel arrays:
Dim strPackages As String = {"Package 1", "Package 2", "Package 3", "Package 4", "Package 5"}
Dim decPrice As String = {100D, 200D, 300D, 400D, 500D}
Private Sub SecondForm_Activated(sender As Object, e As EventArgs) Handles MyBase.Activate
Dim frmMain as New
Dim i As Integer
For i = 0 To strPackages.Length - 1
lstPackages.Items.Add(strPackages(i))
Next
End Sub
After I tried to select a value on lstPackages, but I just was not too sure if I was using the right code to select the item and return it to the main form:
Function CalcPackage()
Dim decPackages As Decimal = 0D
If lstPackages.SelectedIndex = -1 Then
MessageBox.Show("Select a package")
Else
For i = 0 To lstPackages.SelectedItem - 1
decPackages = lstPackages.SelectedItem(i)
Next i
End If
Return decPackages
End Function
When I have selected the item in the lstPackages, I tried to send the selected item back to the main form, but ran into a problem here:
Private Sub btnExit_Click(sender As Object, e As EventArgs) Handles btnClose.Click
Dim frmMainForm As New MainForm
Dim decTotal As Decimal
decTotal = CalcPackage()
'I was stuck in this part and wasn't sure how to return it to lblTotalPrice (and also close the second form too)
frmMainForm.lblTotalPrice.Text = decTotal.ToString("c")
frmMainForm.Show()
Me.Close()
End Sub
So yea sorry if my code is weird, but I'm hoping to get help on how to return the value of the item to lblTotalPrice on the main form, thanks guys
Part of your problem is that you are newing up an instance of the main form in the button exit click event handler of the second form, as shown in this code posted:
Private Sub btnExit_Click(sender As Object, e As EventArgs) Handles btnClose.Click
Dim frmMainForm As New MainForm
Dim decTotal As Decimal
decTotal = CalcPackage()
'I was stuck in this part and wasn't sure how to return it to lblTotalPrice (and also close the second form too)
frmMainForm.lblTotalPrice.Text = decTotal.ToString("c")
frmMainForm.Show()
Me.Close()
End Sub
Instead pass an instance of the main form to the second form via its constructor, like this:
Public Class SecondForm Inherits Form
Dim theMainForm As MainForm
Public Sub New(mainForm As MainForm)
theMainForm = mainForm
End Sub
End Class
Now you can reference the lblTotalPrice in the MainForm class via the theMainForm variable, like this:
theMainForm.lblTotalPrice.Text = lstPackages.SelectedItem
The theMainForm.lblTotalPrice.Text should only be updated in response to an item being selected from lstPackages not when the second form is closed; so handle the updating of the main form's label in the SelectedIndexChanged event of lstPackages, like this:
Protected Sub lstPackages_IndexChanged(sender As Object, e As EventArgs)
Dim decTotal As Decimal
decTotal = CalcPackage()
theMainForm.lblTotalPrice.Text = decTotal.ToString("c")
End Sub
All the exit button click event handler should do is to show the main form and close the second form, like this:
Private Sub btnExit_Click(sender As Object, e As EventArgs) Handles btnClose.Click
theMainForm.Show()
Me.Close()
End Sub
Just display SecondForm with ShowDialog(), then retrieve the value with the reference to it that you already have:
Public Class MainForm
Private Sub Button1_Click(sender As System.Object, e As System.EventArgs) Handles Button1.Click
Dim sf As New SecondForm
If sf.ShowDialog = Windows.Forms.DialogResult.OK Then
lblTotalPrice.Text = sf.TotalPrice
End If
End Sub
End Class
Over in SecondForm:
Public Class SecondForm
Private _TotalPrice As Decimal
Public ReadOnly Property TotalPrice As Decimal
Get
Return _TotalPrice
End Get
End Property
Private Sub btnExit_Click(sender As Object, e As EventArgs) Handles btnClose.Click
If lstPackages.SelectedIndex = -1 Then
MessageBox.Show("Select a package")
Else
_TotalPrice = lstPackages.SelectedItem(0)
Me.DialogResult = Windows.Forms.DialogResult.OK
End If
End Sub
End Class