You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何避免重复打开同一Form?VB.NET动态菜单问题求助

实现MDI窗体中重复点击菜单项不重复打开窗体

我在应用程序中动态添加了MenuStrip,每个菜单项对应打开一个Form。但目前用户每次点击菜单项时,都会新建并打开一个该Form的实例。需要修改代码,实现当目标Form已经处于打开状态时,不再重复创建新实例,而是激活已打开的窗体。

当前现象如下:
每次点击菜单项打开新Form的截图

当前VB.NET代码

Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
    Me.IsMdiContainer = True
    mnuStrip = New MenuStrip() 'create the object
    Me.Controls.Add(mnuStrip)

    con = New SqlConnection("Data Source=\.\SQLExpress,1433;Initial Catalog=MENUDB;Persist Security Info=True;User ID=sa;Password=EZ@123&*")

    Dim sql As String = "select MenuID, MenuName from Menu_Details"
    Dim sda As New SqlDataAdapter(sql, con)
    Dim dt As New DataTable()
    sda.Fill(dt)

    For Each dr As DataRow In dt.Rows
        mnuStripItem = New ToolStripMenuItem(dr("MenuName").ToString())
        SubMenu(mnuStripItem, dr("MenuID").ToString())
        mnuStrip.Items.Add(mnuStripItem)
    Next
    Me.MainMenuStrip = mnuStrip
End Sub

Public Sub SubMenu(mnu As ToolStripMenuItem, mainMenuID As String)
    Dim SqlQuery2 As String = ("select SubMenuName from [SubMenu_Details] where MenuID='") & mainMenuID + "'"
    Dim sdaSqlQuery2 As New SqlDataAdapter(SqlQuery2, con)
    Dim dtSqlQuery2 As New DataTable()
    sdaSqlQuery2.Fill(dtSqlQuery2)

    For Each dr As DataRow In dtSqlQuery2.Rows
        Dim ssmenu As New ToolStripMenuItem(dr("SubMenuName").ToString(), Nothing, New EventHandler(AddressOf MenuClick))
        mnu.DropDownItems.Add(ssmenu)
    Next
End Sub

Private Sub MenuClick(sender As Object, e As EventArgs)
    Dim sqlFormName As String = "select Form_Name,SubMenuName from [SubMenu_Details] where SubMenuName='" + sender.ToString() + "'"
    Dim sdaFormName As New SqlDataAdapter(sqlFormName, con)
    Dim dtFormName As New DataTable()
    sdaFormName.Fill(dtFormName)

    Dim frmAssembly As Assembly = Assembly.LoadFile(Application.ExecutablePath)
    For Each type As Type In frmAssembly.GetTypes()
        If type.BaseType = GetType(Form) Then
            If type.Name = dtFormName.Rows(0)(0).ToString Then
                Dim frmshow As Form = DirectCast(frmAssembly.CreateInstance(type.ToString()), Form)
                frmshow.MdiParent = Me
                frmshow.WindowState = FormWindowState.Maximized
                frmshow.Text = dtFormName.Rows(0)(1).ToString
                frmshow.Show()
            End If
        End If
    Next
End Sub

修改方案

核心逻辑是创建新窗体前,先遍历当前MDI父窗体的子窗体集合,检查是否存在目标类型的已打开窗体:

  1. 获取要打开的窗体类型名称
  2. 遍历Me.MdiChildren查找同类型窗体
  3. 找到则激活并最大化;未找到再创建新实例

修改后的MenuClick方法代码:

Private Sub MenuClick(sender As Object, e As EventArgs)
    Dim sqlFormName As String = "select Form_Name,SubMenuName from [SubMenu_Details] where SubMenuName='" + sender.ToString() + "'"
    Dim sdaFormName As New SqlDataAdapter(sqlFormName, con)
    Dim dtFormName As New DataTable()
    sdaFormName.Fill(dtFormName)

    If dtFormName.Rows.Count = 0 Then Return

    Dim targetFormName As String = dtFormName.Rows(0)(0).ToString()
    Dim targetFormText As String = dtFormName.Rows(0)(1).ToString()

    ' 检查是否已有该类型的MDI子窗体打开
    Dim existingForm As Form = Me.MdiChildren.FirstOrDefault(Function(f) f.GetType().Name = targetFormName)

    If existingForm IsNot Nothing Then
        ' 激活已存在的窗体
        existingForm.Activate()
        ' 确保窗体最大化
        If existingForm.WindowState <> FormWindowState.Maximized Then
            existingForm.WindowState = FormWindowState.Maximized
        End If
        Return
    End If

    ' 没有找到则创建新窗体
    Dim frmAssembly As Assembly = Assembly.LoadFile(Application.ExecutablePath)
    For Each type As Type In frmAssembly.GetTypes()
        If type.BaseType = GetType(Form) AndAlso type.Name = targetFormName Then
            Dim frmshow As Form = DirectCast(frmAssembly.CreateInstance(type.ToString()), Form)
            frmshow.MdiParent = Me
            frmshow.WindowState = FormWindowState.Maximized
            frmshow.Text = targetFormText
            frmshow.Show()
            Exit For ' 找到目标类型后退出循环,无需继续遍历
        End If
    Next
End Sub

额外优化建议

  • 解决SQL注入风险:当前代码直接拼接字符串到SQL语句中,存在安全隐患,建议改用参数化查询:
    ' 示例:修改MenuClick中的SQL查询
    Dim sqlFormName As String = "select Form_Name,SubMenuName from [SubMenu_Details] where SubMenuName=@SubMenuName"
    Dim sdaFormName As New SqlDataAdapter(sqlFormName, con)
    sdaFormName.SelectCommand.Parameters.AddWithValue("@SubMenuName", sender.ToString())
    
  • 变量规范:确保mnuStrip、mnuStripItem、con等变量已在类中正确声明,避免隐式全局变量。

内容的提问来源于stack exchange,提问作者Scorpion_RJ

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.27 16:32:01