如何避免重复打开同一Form?VB.NET动态菜单问题求助
实现MDI窗体中重复点击菜单项不重复打开窗体
我在应用程序中动态添加了MenuStrip,每个菜单项对应打开一个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父窗体的子窗体集合,检查是否存在目标类型的已打开窗体:
- 获取要打开的窗体类型名称
- 遍历
Me.MdiChildren查找同类型窗体 - 找到则激活并最大化;未找到再创建新实例
修改后的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
相关产品推荐
相关产品推荐

