在子表单中发送数据

Send data within Child Forms

我有 3 个窗体,即 MainForm、Form1 和 Form2。 MainForm 在 Panel 中承载 Form1。单击 MainForm 中的 button 时,我将使用 ShowDialog() 方法打开 Form2。现在我在 Form2 中有一个 treeview现在我想将 Form2 中选择的节点传递回 Form1 中的 combobox。如何实现?我在 Form2 中尝试了 Form1.Activate(),但代码没有命中 Form1 中的 Activate 方法。

我也在使用 Form1.ComboBox1.Items.Add(Me.TreeView1.SelectedNode.Text),但是一旦关闭 Form2,我就看不到 ComboBox 中的任何项目。我在这里错过了什么?

为了更好的理解下面的代码。

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

已更新:我已经更新了代码,但仍然没有在 Child Form1 的 ComboBox 中得到任何东西

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

与其在面板上放置 Form,不如考虑创建一个 UserControl 并将其放置在面板上。下面显示如何将数据从使用 ShowDialog() 显示的窗体 (Form2) 传递到存在于不同窗体 (MainForm) 上的用户控件 (UserControl1)。

注意MainForm为启动形式。如果需要,可以将 UserControl 替换为 Form。

新建项目

VS 2019:

  • 在 VS 菜单中,单击 文件

  • Select 新建

  • Select 项目

  • Select Windows 表单应用程序 (.NET Framework)

  • 点击下一步

  • 输入所需的项目名称

  • 单击创建


打开解决方案资源管理器

  • 在VS菜单中,select查看
  • Select 解决方案资源管理器

添加用户控件(名称:UserControl1.vb)

  • 在解决方案资源管理器中,右键单击 <项目名称>,select 添加

  • Select 用户控件(Windows 表单)...(名称:UserControl1.vb)

  • 添加一个Label(文本:“Select一个”)

  • 添加一个ComboBox(名称: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

注意:如果使用 Form 而不是 UserControl,请将 PopulateComboBox 的代码添加到您的表单中。


添加表格(名称:Form2.vb)

  • 在解决方案资源管理器中,右键单击 <项目名称>,select 添加

  • Select表格(Windows表格)...(姓名:Form2.vb)

  • 添加标签

  • 添加一个TreeView(名称:TreeView1)

  • 添加一个按钮(名称:btnAdd;文字:添加)

  • 双击“btnAdd”添加点击事件处理程序

  • 添加一个按钮(名称:btnCancel;文字:取消)

  • 双击“btnCancel”添加点击事件处理程序

在解决方案资源管理器中,右键单击 Form2.vb,然后 select 查看代码

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

将 Form1 重命名为 MainForm

  • 在解决方案资源管理器中,右键单击 Form1.vb
  • Select 重命名
  • 输入MainForm.vb
  • 当提示“您正在重命名一个文件。您是否也想在此项目中对代码元素 'Form1' 的所有引用执行重命名?单击
  • 在属性 Window 中,为“MainForm”设置 Text =“MainForm”

构建项目

  • 在解决方案资源管理器中,右键单击 <项目名称>,select Build

主窗体

  • 向 MainForm 添加面板(名称:panel1)

  • 在工具箱(视图 => 工具箱)中,展开:<解决方案名称> 组件

  • UserControl1 拖到 MainForm

    上的 panel1
  • 添加按钮(名称:btnOpenForm2;文本:Open Form2)

  • 双击“btnOpenForm2”添加 Click 事件处理程序

在解决方案资源管理器中,右键单击 MainForm.vb,然后 select 查看代码

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

演示一下:

资源

  • How to populate a treeview from a list of objects

下面展示了在表单之间传递数据的多种方式。在下面的代码中,我展示了如何使用函数、属性 或事件从子窗体 (ChildForm2) 检索数据。此数据被传递回父窗体 (MainForm)。一旦父窗体 (MainForm) 接收到数据,它将数据发送到不同的子窗体 (ChildForm1)。可以使用构造函数之一、方法或 属性.

将数据从父窗体 (MainForm) 发送到子窗体 (ChildForm1)

注意MainForm 是启动表单并且是 ChildForm1ChildForm2 的父级(即:ChildForm1ChildForm2 是在 MainForm 中创建的)

新建项目

VS 2019:

  • 在 VS 菜单中,单击 文件
  • Select 新建
  • Select 项目
  • Select Windows 表单应用程序 (.NET Framework)

  • 点击下一步
  • 输入所需的项目名称
  • 单击创建

打开解决方案资源管理器

  • 在 VS 菜单中,select 查看
  • Select 解决方案资源管理器

添加表格(姓名:ChildForm1.vb)

  • 在解决方案资源管理器中,右键单击 <项目名称>,select 添加
  • Select表格(Windows表格)...(姓名:ChildForm1.vb)
  • 添加一个Label(文本:“Select一个”)
  • 添加一个ComboBox(名称:ComboBox1)

在解决方案资源管理器中,右键单击 ChildForm1.vb,然后 select 查看代码

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

添加表单(名称:ChildForm2.vb)

  • 在解决方案资源管理器中,右键单击 <项目名称>,select 添加
  • Select表格(Windows表格)...(姓名:ChildForm2.vb)
  • 添加标签
  • 添加一个TreeView(名称:TreeView1)
  • 添加一个按钮(名称:btnAdd;文字:添加)
  • 双击“btnAdd”添加点击事件处理程序
  • 添加一个按钮(名称:btnCancel;文字:取消)
  • 双击“btnCancel”添加 Click 事件处理程序

在解决方案资源管理器中,右键单击 ChildForm2.vb,然后 select 查看代码

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

将 Form1 重命名为 MainForm

  • 在解决方案资源管理器中,右键单击 Form1.vb
  • Select 重命名
  • 输入MainForm.vb
  • 当系统提示“您正在重命名一个文件。您是否也想在此项目中对代码元素 'Form1' 的所有引用执行重命名?单击
  • 在属性 Window 中,为“MainForm”设置 Text =“MainForm”

主窗体

  • 向 MainForm 添加面板(名称:panelFormContainer)
  • 添加按钮(名称:btnOpenChildForm2;文本:Open ChildForm2)
  • 双击“btnOpenChildForm2”添加 Click 事件处理程序

在解决方案资源管理器中,右键单击 MainForm.vb,然后 select 查看代码

在下面的代码中,我以这样一种方式编写它,它允许人们选择不同的选项来从子表单中检索数据,以及将数据发送到子表单的多个选项。

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

选择以下选项之一以从 ChildForm2 检索数据。用下面列出的代码替换方法 btnOpenChildForm2_Click(在 MainForm.vb 中)。

选项 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

注意:当使用Option 1时,DialogForm_BtnAdd_ClickDialogForm_PassValue的代码(在MainForm.vb中)不是使用,所以这两种方法都可以删除。

选项 2(订阅 btnAdd 'Click' 事件)

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

注意:当使用Option 2时,没有使用DialogForm_PassValue的代码(在MainForm.vb中),所以方法DialogForm_PassValue可以删除。

选项 3(订阅事件 '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

注意:当使用Option 3时,没有使用DialogForm_BtnAdd_Click的代码(在MainForm.vb中),所以方法DialogForm_BtnAdd_Click可以删除。


这是一个演示:

资源: