Visual Basic .NET 中动态加载类(二)
应用程序在运行时需要的某些信息可能在编译时无法提供,这些信息通常放置在配置文件中。在 Visual Basic 6.0 中,配置文件应该是 INI 文件或 Windows 注册表。而在 .NET 中,则使用基于 XML 的配置文件。
我们无法详细介绍配置文件,因为这个主题非常复杂。但是,您应该知道,Windows 窗体应用程序的配置文件与应用程序的 EXE 启动文件在同一个目录中。配置文件的名称与程序的 EXE 启动文件的名称相同,只不过在 EXE 文件名后添加了后缀 .config。这就是说,如果执行 MyApp.exe 程序可启动我的应用程序,则配置文件的名称一定是 MyApp.exe.config,而且配置文件必须与 MyApp.exe 位于同一个目录中。
以下是示例中要使用的配置文件:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<configSections>
<section name="availableclasses" type="System.Configuration.NameValueSectionHandler" />
</configSections>
<availableclasses>
<add key="Placeholder – do not load"
value="DLLPathnameGoesHere~NameOfTypeGoesHere"></add>
</availableclasses>
</configuration>
此处,
这实际上并不是存放窗体配置信息的理想方式,因为我们用符号分隔的方式在同一位置保存 DLL 位置和类型名称。但是,使用高级方法分别存放这些信息会要求相当多的注释和代码,所以我们暂且使用这种替代方法。
使用某些文本编辑器或 XML 编辑器(或 Microsoft Visual Studio®)创建上述配置文件,然后使用 FormsOnTheFly.exe.config 文件名将其保存在 FormsOnTheFly 项目的 \bin 目录下。因为 .NET 配置类使用区分大小写的 XML 标记,所以创建此文件时,请注意 XML 标记中字母的大小写。
步骤 4:将配置信息读入集合中 我们为窗体编写的代码将使用 System.Configuration 和 System.Reflection 命名空间中的类。请将以下两行代码置于 Form1 代码的最顶端,以便更方便地访问这些类:
Imports System.Configuration
Imports System.Reflection 还需要一个模块级变量来存放配置信息集合。请将以下代码行紧挨着 代码行放在其下方:
Dim colAvailableClasses As ArrayList 现在,可以编写核心代码了。在 Form1 的 Form Load 事件中放置以下代码,以便读取配置文件、创建存放信息的对象集合以及将集合数据绑定到组合框:
' 实例化配置信息集合。
colAvailableClasses = New ArrayList() ' 获取要从配置文件中加载的可用项。
Dim ClassConfigValues As Specialized.NameValueCollection
ClassConfigValues = CType(ConfigurationSettings.GetConfig("availableclasses"), _
Specialized.NameValueCollection)
Dim iIndex As Integer
Dim sLocation As String
Dim sDescription As String
Dim sType As String
Dim sValue As String
' 创建可绑定到组合框的可用项的
' 集合。
For iIndex = 0 To ClassConfigValues.Count - 1
sDescription = ClassConfigValues.Keys(iIndex)
sValue = ClassConfigValues.Item(sDescription)
' 经过简单的处理,从一个字段中
' 获取位置和类型。
Dim iPos As Integer
iPos = InStr(sValue, "~")
sLocation = Microsoft.VisualBasic.Left(sValue, iPos - 1)
sType = Microsoft.VisualBasic.Right(sValue, Len(sValue) - iPos)
Dim objNewForm As New DynamicClass(sLocation, sDescription, sType)
colAvailableClasses.Add(objNewForm)
Next
' 现在,将集合绑定到组合框。
' 显示说明,并返回对象的引用。
cboForms.DataSource = colAvailableClasses
cboForms.DisplayMember = "Description"
cboForms.ValueMember = "Reference"
步骤 5:插入逻辑以加载所选窗体 现在,在 btnLoadForm 的 click 事件中放置以下逻辑:
Dim objFormToLoad As DynamicClass
objFormToLoad = cboForms.SelectedValue Dim asmAssemblyContainingForm As [Assembly] = _
[Assembly].LoadFrom(objFormToLoad.Location)
Dim TypeToLoad As Type = asmAssemblyContainingForm.GetType(objFormToLoad.Type)
Dim GenericInstance As Object
GenericInstance = Activator.CreateInstance(TypeToLoad)
Dim FormToShow As Form = CType(GenericInstance, Form)
FormToShow.MdiParent = Me
FormToShow.Show()
这是程序的核心部分。通过使用集合中一个对象的信息实例化代码并显示窗体。让我们逐行说明这段代码。
首先我们引用了其中包含要加载窗体的位置和类型的对象 (objFormToLoad)。它被设置为组合框的 SelectedValue 属性,在从数据绑定的组合框返回所选内容时使用。
DLL 的位置包含在对象的 Location 属性中。Assembly 类的 LoadForm 方法使用该属性创建对程序集的引用。(将 Assembly 类置于括号中是因为 Assembly 是 .NET 关键字。括号将通知编译器,其中的内容不是正在使用的关键字,而是类名。)
下面,我们需要引用正在加载的 .NET 类型(类)。可以使用程序集的 GetType 方法,通过传递存放类型名称(该类型名称将从存放配置数据的对象的 Type 属性中获取)的字符串进行引用。对类型的引用保存在 TypeToLoad 中。
Reflection 类和 Activator 类使用它们的 CreateInstance 方法创建类型的实例。(CreateInstance 与 Visual Basic 6.0 中的 CreateObject 类似。)但是,实例必须是类型对象,因为该类型要动态加载。
最后,新实例化的对象(实际上是一个窗体)必须转换为正确的类型才能启用前期绑定。我们知道它是一个窗体,所以可以使用 CType 函数将其转换为窗体。
最后,将新窗体设置为 MDI 父窗体的子窗体并对其进行显示。
Death of the Browser?(英文)所示的 URL 处加载的程序集被复制到本地缓存中。从 UNC 加载的程序集(如本文中的程序集)仅在当前位置使用,不被复制到任何缓存中。步骤 6:编译应用程序 现在,我们可以编译应用程序,但由于尚未创建任何窗体,所以不会显示任何窗体。可以编译并运行程序,确保它能够工作并确保组合框能够正确加载占位符项。如果单击 btnLoadForm,则会显示错误信息或教程,因为配置文件中的信息还未指向任何对象。
步骤 7:创建要显示的窗体 现在,开始创建名为 FirstForm 的新的 Windows 窗体应用程序。在出现的空白 Form1 上放置一些控件 - 控件类型不限。
然后在 Solution Explorer(解决方案资源管理器)中的 FirstForms 项目上单击鼠标右键,选择 Properties(属性)。在 Output Type(输出类型)组合框中选择 Class Library(类库)。如果未看到组合框,可以在 Solution Explorer(解决方案资源管理器)的 Solution(解决方案),而不是 Project(项目)上单击鼠标右键。
现在开始创建项目。即创建一个包含该窗体的 DLL。
创建一个名为 C:\NewForms 的目录。将 FirstForms.dll 从 FirstForms 的 \bin 目录复制到 C:\NewForms 中。
对名为 SecondForm 和 ThirdForm 的项目重复上述操作。在每个窗体中拖入不同的控件以便于区分。也可以将每个窗体的背景色更改为独特的颜色。