New File Functions
As we mentioned before, the Dashboard form handles three tasks of interest:
It obtains a list of DLLs in a directory.
It examines each of the DLLs for Windows forms.
It dynamically loads and shows the form.
Let s see how it performs each of these actions.
Reading the Contents of a Directory
In Visual Basic 6, it is awkward to read the contents of a directory: you have to call the Dir$ function repeatedly. This function returns the name of the first file in the directory, and then the next, and so on. You can t examine two directories at the same time because Dir$ tracks only the file it has enumerated for the current directory. In Visual Basic .NET, getting the list of files in a directory is much simpler. Use the System.IO.Directory.GetFiles method to return an array of filenames. The following sample code is from the Dashboard.GetSubForms method in the DynamicApp project:
Dim filePathArray() As String filePathArray = System.IO.Directory.GetFiles(myDirectory(), "*.dll")
After this code runs, the filePathArray variable is filled with an array of file paths, such as
C:\DynamicShell\bin\GraphicsFeatures.dll
The System.IO namespace contains many other useful functions as well. For example, System.IO.Path.GetDirectoryName returns the directory name from a file path. The code
MsgBox(System.IO.Path.GetDirectoryName( _ "C:\DynamicShell\bin\GraphicsFeatures.dll"))
shows C:\DynamicShell in the message box. In a similar vein, the System.IO.Path.GetFileName method returns the filename of a file path. For example, the code
MsgBox(System.IO.Path.GetFileName( _ "C:\DynamicShell\bin\GraphicsFeatures.dll"))
shows GraphicsFeatures.dll in the message box. Another useful function is System. Reflection.Assembly.GetExecutingAssembly. This last method returns the file path of the application. DynamicApp passes the file path to System. IO.Path.GetDirectoryName to get the application s directory, which is how it knows where to look for DLLs.
Finding All the Forms in a DLL
Once an application has found a DLL, how can it dynamically determine what forms it contains? The enabling technology is called reflection. Reflection encompasses a set of classes that allow you to examine the contents of a DLL at run time. Let s see how it works, using a simplified version of the Dashboard.GetSubForms method in DynamicApp. The following sample shows how to list all of the types (type is the .NET term for class, module, or form) in the current application. Try it out. Create a new Windows application, and enter the following code in the Form_Load event:
Private Sub Form1_Load(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles MyBase.Load Dim asm As Reflection.Assembly Dim typeArray() As Type Dim typeCounter As Integer asm = asm.LoadFrom( _ System.Reflection.Assembly.GetExecutingAssembly.Location) typeArray = asm.GetTypes() For typeCounter = 0 To typeArray.Length - 1 MsgBox(typeArray(typeCounter).Name) Next End Sub
There is only one type in this application: Form1. So when you run this code, Form1 is shown in the message box. What s happening here? Remember that assembly is the .NET term for an EXE or a DLL. We declare a variable of type Assembly and then use it to load the current application. Next we retrieve all of the types in the assembly into an array. We then loop through the array and show the name of each type in a message box. Simple, isn t it?
DynamicApp uses this mechanism to get the list of forms inside every DLL in the bin directory where the application is located. Instead of showing the form name in a message box, it stores the list of forms in an array and uses that array to create a set of buttons on the Dashboard form. When the user clicks a button, DynamicApp shows the appropriate form.
Loading Forms Dynamically
Once you know the name of the DLL and the name of the form you want to show, the rest is quite simple. You create an instance of the form, using the Assembly.CreateInstance method, and then call the Show method on the form. The following code snippet is a simplified version of the code in the Dynamic App Dashboard.Button_Click_Handler method. It demonstrates how to load a DLL called MyDll.dll, create an instance of a form within the DLL Form1, and show the form.
Dim asm As Reflection.Assembly Dim f As Form asm = asm.LoadFrom("MyDll.dll") f = CType(asm.CreateInstance("MyDll.Form1"), Form) f.Show()
Notice with the CreateInstance method that we have to refer to the form as MyDll.Form1, using the <namespace>.<formname> format. Also be aware that when you write code that loads a DLL dynamically, all names, including the filename and type name, are case sensitive.
Reading and Writing to Files
While we re on the subject of the new file functions, let s take a short digression and quickly discuss how to write to and read from files using the new .NET Framework methods. You can still use the existing Visual Basic file functions, but the .NET Framework file methods, although a little more complicated, offer more flexibility when working with files.
We ll create a simple console application that creates a file, writes Hello World to it, closes the file, and then reopens it and shows the contents in a message box. This sample is on the companion CD, and it is called FileReadAndWrite.sln. The project contains one module. Here are the contents of that module:
Sub Main() '* Create a file and write to it Dim outFile As System.IO.FileStream outFile = New System.IO.FileStream("C:\tempFile.txt", _ IO.FileMode.Create, IO.FileAccess.Write) Dim fileWriter As New System.IO.StreamWriter(outFile) fileWriter.WriteLine("Hello World") fileWriter.Close() outFile.Close() '* Open a file and read from it Dim inFile As System.IO.FileStream inFile = New System.IO.FileStream("C:\tempFile.txt", _ IO.FileMode.Open, IO.FileAccess.Read) Dim fileReader As New System.IO.StreamReader(inFile) While fileReader.Peek > -1 MsgBox(fileReader.ReadLine) End While fileReader.Close() inFile.Close() End Sub
To open a file for writing, you have to perform two steps: create a stream object and then create a StreamWriter object to write to the stream. You can then write to the file using the StreamWriter object s Write and WriteLine methods. Reading from a file involves a similar process: create a stream object, and then create a StreamReader to read from the stream. To determine whether there is anything to read from the file, use the StreamReader object s Peek method, which returns the value of the next byte in the file, or 1 if there is nothing left to read. The StreamReader object s Read, ReadBlock, ReadLine, and ReadToEnd methods are used to read the contents of the file.
Using Dynamic Properties
Let s take one more digression and look at dynamic properties. With Windows Forms you have the ability to change the properties of the form at run time, using a configuration file. Figure 18-2 shows the DynamicApp solution open in Visual Basic .NET. Notice that the solution has a file called App.config, and notice also that in the Property Browser, the Form.Text property has a small blue icon next to the name. This indicates that the Form.Text property is retrieved at run time from the App.config file.
Figure 18-2
The App.config file and the Form.Text dynamic property.
If you double-click the App.config file to open it, you will see that it is an XML file and that it has the following line in the body:
<add key="Dashboard.Text" value="DynamicApp" />
This is the value of the Text property, which is used to set the title bar text of the application. When the project is compiled, this file is deployed alongside the EXE, with the name DynamicApp.exe.config. If you open this file in Notepad and change the setting to something like
<add key="Dashboard.Text" value="Hello World" />
Hello World will show in the title bar the next time the application is run. It is called a dynamic property because the value is not stored in the compiled application but instead in an editable XML file that the application reads at run time.
Why is the Text property dynamic? Are all properties dynamic? When you create a form, by default none of the properties are dynamic. To make a particular property dynamic, you use the (Dynamic Properties) property in the Property Browser; you use the (Advanced) builder to select the properties that you want to make dynamic, as shown in Figure 18-3.
Figure 18-3
Making properties dynamic.
Dynamic properties are valuable for database connection strings, Internet addresses, directory names, and any other property that might need to be changed after the application is compiled.