在主窗体中加载窗体的最佳实践,两者都驻留在外部 DLL 程序集中

Best practices on load a form inside a main form, both residing in an external DLL assembly

我已经像这样将外部 DLL 加载到您的项目中

Dim assembly As Assembly = Assembly.LoadFile(libraryPath & "filename.dll")
Dim type As Type = assembly.[GetType]("NameSpace.ClassName")

// i'm loading as a form here but can be any control
Dim frm As Form = TryCast(Activator.CreateInstance(type), Form)

如果您要加载一个 class 或加载的窗体(上图)中的控件,而该窗体也位于同一程序集中,最佳做法是什么?

例如: 假设您加载了一个由多个表单组成的程序集。您加载程序集并创建其中一种窗体 frm 的实例。但是在加载的表单中,frm 还加载了另一个第二个表单,frm2 也属于同一个程序集。我如何实例化第二种形式?我需要重新加载程序集吗?

编辑: 大会是一个。 Net 4.8 class 库,包含多种形式、模块和抽象 classes。正在加载到也在 .Net 4.8 中的 winforms 中。 此问题适用于 VB 和 C#。但就我而言,我在 VB.

上编码

您可以在应用程序的启动事件中加载外部程序集。
打开 Project->Properties->Application 并单击 View Application Events 按钮。
这将打开或生成 ApplicationEvents.vb class 文件(您会在一些评论中看到,使用 My.Application class 更经常处理的事件)。

在这里,我们向 StartUp 事件添加一个处理程序:在加载和显示应用程序启动窗体之前引发此事件。
我们定义了一个 Public class 对象,它引用包含表单资源的外部库。
通过 My.Application 引用,此 public class 对象对应用程序中的所有其他 class 可见。
此 class 公开了一个 Public 方法,LoadForm(),允许按名称加载外部表单。

Imports System.IO
Imports System.Reflection
Imports Microsoft.VisualBasic.ApplicationServices

Namespace My
    Partial Friend Class MyApplication

        Public FormResources As ResourceBag

        Protected Overrides Function OnStartup(e As StartupEventArgs) As Boolean
            FormResources = New ResourceBag()
            Return MyBase.OnStartup(e)
        End Function

        Friend Class ResourceBag
            Private Shared libraryPath As String = "[Your Library path]"
            Private Shared asm As Assembly = Nothing
            Public Sub New()
                Dim resoursePath As String = Path.Combine(libraryPath, "[Your .dll Name].dll")
                If File.Exists(resoursePath) Then
                    asm = Assembly.LoadFile(resoursePath)
                End If
            End Sub

            Public Function LoadForm(formName As String) As Form
                If asm Is Nothing Then Throw New BadImageFormatException("Resource Library not loaded")
                Return TryCast(Activator.CreateInstance(asm.GetType($"[Library Namespace].{formName}")), Form)
            End Function
        End Class
    End Class
End Namespace

现在,假设您的外部库中有一个名为 Form1 的表单。
您可以 Form1 使用 LoadForm() 方法(它在外部库中创建一个表单实例)或 New 从外部库加载另一个表单或从程序集中加载一个表单用于创建本地表单实例的关键字 class.

► 如果外部库的 Form1 已经显示了同一个库中的另一个表单,例如,单击一个按钮,我们不需要做任何事情:外部新表单照常生成。

在下面的示例代码中,我们改为向外部 Form1 添加两个按钮 class:

  • 一个按钮使用先前定义的 public 方法从外部库加载表单 Form2Using extForm1 As Form = My.Application.FormResources.LoadForm("Form1")
  • 另一个按钮加载一个也名为 Form2 的表单,它是当前应用程序的一部分,使用 New 关键字:Dim f2 As New Form2()

► 外部Form1实例是用Using语句创建的,因为ShowDialog()方法是用来显示新Form的:这就需要我们在关闭时销毁Form,否则无法销毁自己什么时候这样显示。
如果您使用 Show()Show(Me),则需要删除 Using 块,否则表单将立即关闭。表单及其 Controls/components 无论如何都会在关闭时被处理掉。

► 您的应用程序的 Form2 表单 class 不能从应用程序设置中添加控件(如果第二次显示表单,这些控件将被处理并生成异常)


Using extForm1 As Form = My.Application.FormResources.LoadForm("Form1")
    If extForm1 IsNot Nothing Then
        Dim btnExt = New Button() With {
            .Location = New Point(20, 50),
            .Size = New Size(100, 30),
            .Text = "Lib Form2"
        }
        AddHandler btnExt.Click,
            Sub()
                Dim f2ext = My.Application.FormResources.LoadForm("Form2")
                f2ext.Show()
            End Sub

        Dim btn = New Button() With {
            .Location = New Point(140, 50),
            .Size = New Size(100, 30),
            .Text = "My Form2"
        }
        AddHandler btn.Click,
            Sub()
                Dim f2 As New Form2()
                f2.Show()
            End Sub

        extForm1.Controls.AddRange({btnExt, btn})
        btnExt.BringToFront()
        btn.BringToFront()
        extForm1.StartPosition = FormStartPosition.CenterParent
        extForm1.ShowDialog(Me)
    End If
End Using