Chapter 7. Namespaces

Even if you did not realize it, you have been using namespaces since the beginning of this book. For example, System, System.Diagnostics, and System.Windows.Forms are all namespaces contained within the .NET Framework. Namespaces are an easy concept to understand, but this chapter puts the ideas behind them on a firm footing — and clears up any misconceptions you might have about how they are used and organized.

If you are familiar with COM, you will find that the concept of namespaces is the logical extension of programmatic identifier (ProgID) values. For example, the functionality of Visual Basic 6's FileSystemObject is now mostly encompassed in .NET's System.IO namespace, though this is not a one-to-one mapping. However, namespaces reflect more than a change in name; they represent the logical extension of the COM naming structure, expanding its ease of use and extensibility.

In addition to the traditional System and Microsoft namespaces (for example, used in things such as Microsoft's Web Services Enhancements), the .NET Framework 3.5 includes a way to access some tough-to-find namespaces using the My namespace. The My namespace is a powerful way of "speed-dialing" specific functionalities in the base.

This chapter about namespaces covers the following:

  • What namespaces are

  • Which namespaces are used in Visual Studio 2008 projects by default

  • How to reference namespaces and use the Imports statement

  • How the compiler searches for class references

  • How to alias namespaces and create your own namespaces

  • How to use the My namespace

What Is a Namespace?

Namespaces are a way of organizing the vast number of classes, structures, enumerations, delegates, and interfaces that the .NET Framework class library provides. They are a hierarchically structured index into a class library, which is available to all of the .NET languages, not only the Visual Basic 2008 language (with the exception of the My namespace). The namespaces, or object references, are typically organized by function. For example, the System.IO namespace contains classes, structures, and interfaces for working with input/output streams and files. The classes in this namespace do not necessarily inherit from the same base classes (apart from Object, of course).

A namespace is a combination of a naming convention and an assembly, which organizes collections of objects and prevents ambiguity about object references. A namespace can be, and often is, implemented across several physical assemblies, but from the reference side, it is the namespace that ties these assemblies together. A namespace consists of not only classes, but also other (child) namespaces. For example, IO is a child namespace of the System namespace.

Namespaces provide identification beyond the component name. With a namespace, it is possible to use a more meaningful title (for example, System) followed by a grouping (for example, Text) to group together a collection of classes that contain similar functions. For example, the System.Text namespace contains a powerful class called StringBuilder. To reference this class, you can use the fully qualified namespace reference of System.Text.StringBuilder, as shown here:

Dim sb As New System.Text.StringBuilder()

The structure of a namespace is not a reflection of the physical inheritance of classes that make up the namespace. For example, the System.Text namespace contains another child namespace called RegularExpressions. This namespace contains several classes, but they do not inherit or otherwise reference the classes that make up the System.Text namespace.

Figure 7-1 shows how the System namespace contains the Text child namespace, which also has a child namespace, called RegularExpressions. Both of these child namespaces, Text and RegularExpressions, contain a number of objects in the inheritance model for these classes, as shown in the figure.

As shown in Figure 7-1, while some of the classes in each namespace do inherit from each other, and while all of the classes eventually inherit from the generic Object, the classes in System.Text.RegularExpressions do not inherit from the classes in System.Text.

To emphasize the usefulness of namespaces, we can draw another good example from Figure 7-1 . If you make a reference to System.Drawing.Imaging.Encoder in your application, then you are making a reference to a completely different Encoder class than the namespace shown in Figure 7-1System.Text.Encoder. Being able to clearly identify classes that have the same name but very different functions, and disambiguate them, is yet another advantage of namespaces.

If you are an experienced COM developer, you may note that unlike a ProgID, which reflects a one-level relationship between the project assembly and the class, a single namespace can use child namespaces to extend the meaningful description of a class. The System namespace, imported by default as part of every project created with Visual Studio, contains not only the default Object class, but also many other classes that are used as the basis for every .NET language.

What if a class you need isn't available in your project? The problem may be with the references in your project. For example, by default, the System.DirectoryServices namespace, used to get programmatic access to the Active Directory objects, is not part of your project's assembly. Using it requires adding a reference to the project assembly. The concept of referencing a namespace is very similar to the capability to reference a COM object in VB6.

Figure 7-1

Figure 7.1. Figure 7-1

In fact, with all this talk about referencing, it is probably a good idea to look at an example of adding an additional namespace to a project. Before doing that, though, you should know a little bit about how a namespace is actually implemented.

Namespaces are implemented within .NET assemblies. The System namespace is implemented in an assembly called System.dll provided with Visual Studio. By referencing this assembly, the project is capable of referencing all the child namespaces of System that happen to be implemented in this assembly. Using the preceding table, the project can import and use the System.Text namespace because its implementation is in the System.dll assembly. However, although it is listed, the project cannot import or use the System.Data namespace unless it references the assembly that implements this child of the System namespace, System.Data.dll.

Let's create a sample project so you can examine the role that namespaces play within it. Using Visual Studio 2008, create a new Visual Basic Windows Application project called Namespace_Sampler.

The Microsoft.VisualBasic.Compatibility.VB6 library is not part of Visual Basic 2008 projects by default. To gain access to the classes that this namespace provides, you need to add it to your project. You can do this by using the Add Reference dialog box (available by right-clicking the Project Name node within the Visual Studio Solution Explorer). The Add Reference dialog box has five tabs, each containing elements that can be referenced from your project:

  • .NET — This tab contains .NET assemblies that can be found in the GAC. In addition to providing the name of the assembly, you can also get the version of the assembly and the version of the framework to which the assembly is compiled. The final data point found in this tab is the location of the assembly on the machine.

  • COM — This tab contains all the available COM components. It provides the name of the component, the TypeLib version, and the path of the component.

  • Projects — This tab contains any custom .NET assemblies from any of the various projects contained within your solution.

  • Browse — This tab enables you to look for any component files (.dll, .tlb, .olb, .ocx, .exe, or .manifest) on the network.

  • Recent — This tab lists the most recently made references for quick referencing capabilities.

The Add Reference dialog is shown in Figure 7-2.

Figure 7-2

Figure 7.2. Figure 7-2

The available .NET namespaces are listed by component name. This is the same as the namespace name. Within the dialog, you can see a few columns that supply the namespace of the component, the version number of the component, the version of the .NET Framework for which the particular component is targeted, and the path location of the file. You can select a single namespace to make a reference to by clicking your mouse on the component that you are interested in. Holding down the Ctrl key and pressing the mouse button enables you to select multiple namespaces to reference.

To select a range of namespaces, first click on either the first or the last component in the dialog that is contained in the range, and then complete the range selection by holding down the Shift key and using the mouse to select the other component in the range. Once you have selected all the components that you are interested in referencing, click OK.

The example in Figure 7-2 is importing some namespaces from the Microsoft.VisualBasic namespace, even though only one selection has been made. This implementation, while a bit surprising at first, is very powerful. First, it shows the extensibility of namespaces. This is because the single Microsoft.VisualBasic.Compatibility.VB6 namespace is actually implemented in two separate assemblies. If you also make a reference to the Microsoft.VisualBasic.Compatibility namespace as well as the Microsoft.VisualBasic.Compatibility.Data namespace, you will see (through the Object Browser found in Visual Studio) that the Microsoft.VisualBasic.Compatibility.VB6 namespace is actually found in both locations, as shown in Figure 7-3.

Figure 7-3

Figure 7.3. Figure 7-3

Second, this implementation enables you to include only the classes that you need — in this case, those related to the VB6 (Visual Basic 6) environment or to database tools, or both types.

Note some interesting points about the Microsoft.VisualBasic namespace. First, this namespace gives you access to all the functions that VB6 developers have had for years. Microsoft has implemented these in the .NET Framework and made them available for your use within your .NET projects. Because these functions have been implemented in the .NET Framework, there is absolutely no performance hit for using them, but you will most likely find the functionality that they provide available in newer .NET namespaces. Second, contrary to what the name of the namespace suggests, this namespace is available for use by all of the .NET languages, which means that even a C# developer could use the Microsoft.VisualBasic namespace if desired.

Namespaces and References

Highlighting their importance to every project, references (including namespaces) are no longer hidden from view, available only after opening a dialog box as they were in VB6. As shown in the Solution Explorer window in Figure 7-4, every new project includes a set of referenced namespaces. (If you do not see the references listed in the Solution Explorer, click the Show All Files button from the Solution Explorer menu.)

The list of default references varies depending on the type of project. The example in Figure 7-4 shows the default references for a Windows Forms project in Visual Studio 2008. If the project type were an ASP.NET Web Application, the list of references would change accordingly — the reference to the System.Windows.Forms namespace assembly would be replaced by a reference to System.Web. If the project type were an ASP.NET Web service (not shown), then the System.Windows.Forms namespace would be replaced by references to the System.Web and System.Web.Services namespaces.

Figure 7-4

Figure 7.4. Figure 7-4

In addition to making the namespaces available, references play a second important role in your project. One of the advantages of .NET is using services and components built on the common language runtime (CLR), which enables you to avoid DLL conflicts. The various problems that can occur related to DLL versioning, commonly referred to as "DLL hell," involve two types of conflict.

The first situation occurs when you have a component that requires a minimum DLL version, and an older version of the same DLL causes your product to break. The alternative situation is when you require an older version of a DLL, and a new version is incompatible. In either case, the result is that a shared file, outside of your control, creates a systemwide dependency that affects your software. With .NET, it is possible, but not required, to indicate that a DLL should be shipped as part of your project to avoid an external dependency.

To indicate that a referenced component should be included locally, you can select the reference in the Solution Explorer and then examine the properties associated with that reference. One editable property is called Copy Local. You will see this property and its value in the Properties window within Visual Studio 2008. For those assemblies that are part of a Visual Studio 2008 installation, this value defaults to False, as shown in Figure 7-5 . However, for custom references, this property defaults to True to indicate that the referenced DLL should be included as part of the assembly. Changing this property to True changes the path associated with the assembly. Instead of using the path to the referenced file's location on the system, the project creates a subdirectory based on the reference name and places the files required for the implementation of the reference in this subdirectory.

The benefit of this is that even when another version of the DLL is later placed on the system, your project's assembly will continue to function. However, this protection from a conflicting version comes at a price: Future updates to the namespace assembly to fix flaws will be in the system version but not in the private version that is part of your project's assembly.

To resolve this, Microsoft's solution is to place new versions in directories based on their version information. If you examine the path information for all of the Visual Studio 2008 references, you will see that it includes a version number. As new versions of these DLLs are released, they are installed in a separate directory. This method allows for an escape from DLL hell, by keeping new versions from stomping on old versions, and it enables old versions to be easily located for maintenance updates. Therefore, it is often better to leave alone the default behavior of Visual Studio 2008, which is set to copy only locally custom components, until your organization implements a directory structure with version information similar to that of Microsoft.

Figure 7-5

Figure 7.5. Figure 7-5

The Visual Basic 2008 compiler will not allow you to add a reference to your assembly if the targeted implementation includes a reference that is not also referenced in your assembly. The good news is that the compiler will help. If, after adding a reference, that reference does not appear in the IntelliSense list generated by Visual Studio 2008, then go ahead and type the reference to a class from that reference. The compiler will flag it with underlining, similar to the Microsoft Word spelling or grammar error underlines. When you click the underlined text, the compiler will tell you which other assemblies need to be referenced in the project in order to use the class in question.

Common Namespaces

The generated list of references shown in the Solution Explorer for the newly created Namespace_Sampler project includes most, but not all, of the namespaces that are part of your Windows Application project. For example, one namespace not displayed as a reference is Microsoft.VisualBasic, and the accompanying Microsoft.VisualBasic.dll. Every Visual Basic 2008 project includes the namespace Microsoft.VisualBasic. This namespace is part of the Visual Studio project templates for Visual Basic 2008 and is, in short, what makes Visual Basic 2008 different from C# or any other .NET language. The implicit inclusion of this namespace is the reason why you can call IsDBNull and other methods of Visual Basic 2008 directly. The only difference in the default namespaces included with Visual Basic 2008 and C# Windows Application projects is that the former use Microsoft.VisualBasic and the latter use Microsoft.CSharp.

To see all of the namespaces that are imported automatically, such as the Microsoft.VisualBasic namespace, right-click the project name in the Solution Explorer and select Properties from the context menu. This opens the project's Properties window in Visual Studio. Select the References tab from the left pane and you will see the reference Microsoft.VisualBasic at the top of the list (see Figure 7-6).

Figure 7-6

Figure 7.6. Figure 7-6

When looking at the project's global list of imports in the text area at the bottom of the page, you can see that in addition to the Microsoft.VisualBasic namespace, the System.Collections and System.Diagnostics namespaces are also imported into the project. This is signified by the check marks next to the namespace. Unlike the other namespaces in the list, these namespaces are not listed as references in the text area directly above this. That's because implementation of the System.Collections and System.Diagnostics namespaces is part of the referenced System.dll. Similarly to Microsoft.VisualBasic, importing these namespaces allows references to the associated classes, such that a fully qualified path is not required. Because these namespaces contain commonly used classes, it is worthwhile to always include them at the project level.

The following list briefly summarizes some of the namespaces commonly used in Visual Basic 2008 projects:

  • System.Collections — Contains the classes that support various feature-rich object collections. Included automatically, it has classes for arrays, lists, dictionaries, queues, hash tables, and so on.

  • System.Collections.Generic — Ever since .NET 2.0, this namespace has enabled working with the generics capabilities of the framework — a way to build type-safe collections as well as provide generic methods and classes.

  • System.Data — This namespace contains the classes needed to support the core features of ADO.NET.

  • System.Diagnostics — Included in all Visual Basic 2008 projects, this namespace includes the debugging classes. The Trace and Debug classes provide the primary capabilities, but the namespace contains dozens of classes to support debugging.

  • System.Drawing — This namespace contains simple drawing classes to support Windows Application projects.

  • System.EnterpriseServices — Not included automatically, the System.EnterpriseServices implementation must be referenced to make it available. This namespace contains the classes that interface .NET assemblies with COM+.

  • System.IO — This namespace contains important classes that enable you to read and write to files as well as data streams.

  • System.Linq — This namespace contains an object interface to work with disparate data sources in a new and easy manner. This is a new namespace in .NET 3.5.

  • System.Text — This commonly used namespace enables you to work with text in a number of different ways, usually in regard to string manipulation. One of the more popular objects that this namespace offers is the StringBuilder object.

  • System.Threading — This namespace contains the objects needed to work with and manipulate threads within your application.

  • System.Web — This is the namespace that deals with one of the more exciting features of the .NET Framework: ASP.NET. This namespace provides the objects that deal with browser-server communications. Two main objects include HttpRequest, which deals with the request from the client to the server, and HttpResponse, which deals with the response from the server to the client.

  • System.Web.Services — This is the main namespace you use when creating XML Web Services, one of the more powerful capabilities provided with the .NET Framework. This namespace offers the classes that deal with SOAP messages and the manipulation of these messages.

  • System.Windows.Forms — This namespace provides classes to create Windows Forms in Windows Application projects. It contains the form elements.

Of course, to really make use of the classes and other objects in this list, you need more detailed information. In addition to resources such as Visual Studio 2008's help files, the best source of information is the Object Browser, available directly in the Visual Studio 2008 IDE. You can find it by selecting View

Figure 7-6

The Object Browser displays each of the referenced assemblies and enables you to drill down into the various namespaces. Figure 7-7 illustrates how the System.dll implements a number of namespaces, including some that are part of the System namespace. By drilling down into a namespace, you can see some of the classes available. By further selecting a class, the browser shows not only the methods and properties associated with the selected class, but also a brief outline of what that class does.

Using the Object Browser is an excellent way to gain insight into which classes and interfaces are available via the different assemblies included in your project, and how they work. Clearly, the ability to actually see which classes are available and know how to use them is fundamental to being able to work efficiently. Working effectively in the .NET CLR environment requires finding the right class for the task.

Figure 7-7

Figure 7.7. Figure 7-7

Importing and Aliasing Namespaces

Not all namespaces should be imported at the global level. Although you have looked at the namespaces included at this level, it is much better to import namespaces only in the module where they will be used. Importing a namespace at the module level does not change setting the reference, but you do not add it into the list of imports on the project's Properties page. As with variables used in a project, it is possible to define a namespace at the module level. The advantage of this is similar to using local variables in that it helps to prevent different namespaces from interfering with each other. As this section shows, it is possible for two different namespaces to contain classes or even child namespaces with the same name.

Importing Namespaces

The development environment and compiler need a way to prioritize the order in which namespaces should be checked when a class is referenced. It is always possible to unequivocally specify a class by stating its complete namespace path. This is referred to as fully qualifying your declaration. The following example fully qualifies a StringBuilder object:

Dim sb As New System.Text.StringBuilder

However, if every reference to every class needed its full namespace declaration, then Visual Basic 2008 and every other .NET language would be very difficult to program in. After all, who wants to type System.Collections.ArrayList each time an instance of the ArrayList class is wanted? If you review the global references, you will see the System.Collections namespace. Thus, you can just type ArrayList whenever you need an instance of this class, as the reference to the larger System.Collections namespace has already been made by the application.

In theory, another way to reference the StringBuilder class is to use Text.StringBuilder, but with all namespaces imported globally, there is a problem with this, caused by what is known as namespace crowding. Because there is a second namespace, System.Drawing, that has a child called Text, the compiler does not have a clear location for the Text namespace and, therefore, cannot resolve the StringBuilder class. The solution to this problem is to ensure that only a single version of the Text child namespace is found locally. That way, the compiler will use this namespace regardless of the global availability of the System.Drawing.Text namespace.

Imports statements specify to the compiler those namespaces that the code will use:

Imports Microsoft.Win32
Imports System
Imports SysDraw = System.Drawing

Once they are imported into the file, you are not required to fully qualify your object declarations in your code. For instance, if you imported the System.Data.SqlClient namespace into your file, then you would be able to create a SqlConnection object in the following manner:

Dim conn As New SqlConnection

Each of the preceding Imports statements illustrates a different facet of importing namespaces. The first namespace, Imports Microsoft.Win32, is not imported at the global level. Looking at the reference list, you may not see the Microsoft assembly referenced directly. However, opening the Object Browser reveals that this namespace is actually included as part of the System.dll.

As noted earlier, the StringBuilder references become ambiguous because both System.Text and System.Drawing.Text are valid namespaces at the global level. As a result, the compiler has no way to determine which Text child namespace is being referenced. Without any clear indication, the compiler flags Text.StringBuilder declarations in the command handler. However, using the Imports System declaration in the module tells the compiler that before checking namespaces imported at the global level, it should attempt to match incomplete references at the module level. Because the System namespace is declared at this level, if System.Drawing is not, then there is no ambiguity regarding which child namespace Text.StringBuilder belongs to.

This sequence demonstrates how the compiler looks at each possible declaration:

  • It first determines whether the item is a complete reference, such as System.Text.StringBuilder.

  • If the declaration does not match a complete reference, then the compiler tries to determine whether the declaration is from a child namespace of one of the module-level imports.

  • Finally, if a match is not found, then the compiler looks at the global-level imports to determine whether the declaration can be associated with a namespace imported for the entire assembly.

While the preceding logical progression of moving from a full declaration through module- to global-level imports resolves the majority of issues, it does not handle all possibilities. Specifically, if you import System.Drawing at the module level, the namespace collision would return. This is where the third import statement becomes important — this import statement uses an alias.

Aliasing Namespaces

Aliasing has two benefits in .NET. First, aliasing enables a long namespace such as System.EnterpriseServices to be replaced with a shorthand name such as COMPlus. Second, it adds a way to prevent ambiguity among child namespaces at the module level.

As noted earlier, the System and System.Drawing namespaces both contain a child namespace of Text. Because you will be using a number of classes from the System.Drawing namespace, it follows that this namespace should be imported into the form's module. However, were this namespace imported along with the System namespace, the compiler would again find references to the Text child namespace ambiguous. By aliasing the System.Drawing namespace to SysDraw, the compiler knows that it should only check the System.Drawing namespace when a declaration begins with that alias. The result is that although multiple namespaces with the same child namespace are now available at the module level, the compiler knows that one (or more) of them should only be checked at this level when they are explicitly referenced.

Aliasing as defined here is done in the following fashion:

Imports SysDraw = System.Drawing

Referencing Namespaces in ASP.NET

Making a reference to a namespace in ASP.NET is quite similar to working with Windows Forms, but you have to take some simple, additional steps. From your ASP.NET solution, first make a reference to the assemblies from the References folder, just as you do with Windows Forms. Once there, import these namespaces at the top of the page file in order to avoid having to fully qualify the reference every time on that particular page.

For example, instead of using System.Collections.Generic for each instance of use, use the < %# Import % > page directive at the top of the ASP.NET page (if the page is constructed using the inline coding style) or use the Imports keyword at the top of the ASP.NET page's code-behind file (just as you would with Windows Forms applications). The following example shows how to perform this task when using inline coding for ASP.NET pages:

<%# Import Namespace="System.Collections.Generic" %>

Now that this reference is in place on the page, you can access everything this namespace contains without having to fully qualify the object you are accessing. Note that the Import keyword in the inline example is not missing an "s" at the end. When importing in this manner, it is Import (without the "s") instead of Imports — as it is in the ASP.NET code-behind model and Windows Forms.

In ASP.NET 1.0/1.1, if you used a particular namespace on each page of your application, you need the Import statement on each and every page where that namespace was needed. ASP.NET 3.5 includes the capability to use the web.config file to make a global reference so that you don't need to make further references on the pages themselves, as shown in the following example:

<pages>
   <namespaces>
      <add namespace="System.Drawing" />
      <add namespace="Wrox.Books" />
   </namespaces>
</pages>

In this example, using the <namespaces> element in the web.config file, references are made to the System.Drawing namespace and the Wrox.Books namespace. Because these references are now contained within the web.config file, there is no need to again reference them on any of the ASP.NET pages contained within this solution.

Creating Your Own Namespaces

Every assembly created in .NET is part of some root namespace. By default, this logic actually mirrors COM, in that assemblies are assigned a namespace that matches the project name. However, unlike COM, in .NET it is possible to change this default behavior. Just as Microsoft has packaged the system-level and CLR classes using well-defined names, you can create your own namespaces. Of course, it is also possible to create projects that match existing namespaces and extend those namespaces, but that is very poor programming practice.

Creating an assembly in a custom namespace can be done at one of two levels, although unless you want the same name for each assembly that will be used in a large namespace, you would normally reset the root namespace for the assembly. This is done through the assembly's project pages, reached by right-clicking the solution name in the Solution Explorer window and working off the first tab (Application) within the Properties page that opens in the document window, as shown in Figure 7-8.

The next step is optional, but, depending on whether you want to create a class at the top level or at a child level, you can add a Namespace command to your code. There is a trick to being able to create top-level namespaces or multiple namespaces within the modules that make up an assembly. Instead of replacing the default namespace with another name, you can delete the default namespace and define the namespaces only in the modules, using the Namespace command.

The Namespace command is accompanied by an End Namespace command. This End Namespace command must be placed after the End Class tag for any classes that will be part of the namespace. The following code demonstrates the structure used to create a MyMetaNamespace namespace, which contains a single class:

Namespace MyMetaNamespace
    Class MyClass1
       ' Code
    End Class
End Namespace
Figure 7-8

Figure 7.8. Figure 7-8

You can then utilize the MyClass1 object simply by referencing its namespace, MyMetaNamespace.MyClass1. It is also possible to have multiple namespaces in a single file, as shown here:

Namespace MyMetaNamespace1
    Class MyClass1
       ' Code
    End Class
 End Namespace

Namespace MyMetaNamespace2
   Class MyClass2
      ' Code
    End Class
 End Namespace

Using this kind of structure, if you want to utilize MyClass1, then you access it through the namespace MyMetaNamespace.MyClass1. This does not give you access to MyMetaNamespace2 and the objects that it offers; instead, you have to make a separate reference to MyMetaNamespace2.MyClass2.

The Namespace command can also be nested. Using nested Namespace commands is how child namespaces are defined. The same rules apply — each Namespace must be paired with an End Namespace and must fully encompass all of the classes that are part of that namespace. In the following example, the MyMetaNamespace has a child namespace called MyMetaNamespace.MyChildNamespace:

Namespace MyMetaNamespace
    Class MyClass1
       ' Code
    End Class

    Namespace MyChildNamespace
Class MyClass2
           ' Code
       End Class
    End Namespace
End Namespace

This is another point to be aware of when you make references to other namespaces within your own custom namespaces. Consider the following example:

Imports System
Imports System.Data
Imports System.Data.SqlClient
Imports System.IO

Namespace MyMetaNamespace1
    Class MyClass1
       ' Code
    End Class
End Namespace
Namespace MyMetaNamespace2
   Class MyClass2
      ' Code
   End Class
End Namespace

In this example, a number of different namespaces are referenced in the file. The three namespaces referenced at the top of the code listing — the System, System.Data, and System.Data.SqlClient namespace references — are available to every namespace developed in the file. This is because these three references are sitting outside of any particular namespace declarations. However, the same is not true for the System.IO namespace reference. Because this reference is made within the MyMetaNamespace2 namespace, it is unavailable to any other namespace in the file.

Note

When you create your own namespaces, Microsoft recommends that you use a convention of CompanyName.TechnologyName — for example, Wrox.Books. This helps to ensure that all libraries are organized in a consistent way.

Sometimes when you are working with custom namespaces, you might find that you have locked yourself out of accessing a particular branch of a namespace, purely due to naming conflicts. Visual Basic includes the Global keyword, which can be used as the outermost root class available in the .NET Framework class library. Figure 7-9 shows a diagram of how the class structure looks with the Global keyword.

This means that you can make specifications such as

Global.System.String

or

Global.Wrox.System.Titles
Figure 7-9

Figure 7.9. Figure 7-9

The My Keyword

The My keyword is a novel concept that was introduced in the .NET Framework 2.0 to quickly give you access to your application, your users, your resources, the computer, or the network on which the application resides. The My keyword has been referred to as a way of speed-dialing common but complicated resources to which you need access. Using the My keyword, you can quickly access a wide variety of items, such as user details or specific settings of the requestor's browser.

Though not really considered a true namespace, the My object declarations that you make work in the same way as the .NET namespace structure you are used to working with. To give you an example, let's first look at how you get the user's machine name using the traditional namespace structure:

Environment.MachineName.ToString()

For this example, you simply need to use the Environment class and use this namespace to get at the MachineName property. The following shows how you would accomplish this same task using the My keyword:

My.Computer.Info.MachineName.ToString()

Looking at this example, you may be wondering what the point is if the example that uses My is lengthier than the first example that just works off of the Environment namespace. Remember that the point is not the length of what you type to access specific classes, but a logical way to find frequently accessed resources without spending a lot of time hunting them down. Would you have known to look in the Environment class to get the machine name of the user's computer? Maybe, but maybe not. Using My.Computer.Info.MachineName.ToString is a tremendously more logical approach; and once compiled, this namespace declaration will be set to work with the same class as shown previously without a performance hit.

If you type the My keyword in your Windows Forms application, IntelliSense provides you with seven items to work with: Application, Computer, Forms, Resources, Settings, User, and WebServices. Though this keyword works best in the Windows Forms environment, there are still things that you can use in the Web Forms world. If you are working with a Web application, then you will have three items off the My keyword: Application, Computer, and User. Each of these is described further in the following sections.

My.Application

The My.Application namespace gives you quick access to specific settings and points that deal with your overall application. The following table details the properties and methods of the My.Application namespace:

Property/Method

Description

ApplicationContext

Returns contextual information about the thread of the Windows Forms application

ChangeCulture

A method that enables you to change the culture of the current application thread

ChangeUICulture

A method that enables you to change the culture that is being used by the Resource Manager

Culture

Returns the current culture being used by the current thread

Deployment

Returns an instance of the ApplicationDeployment object, which allows for programmatic access to the application's ClickOnce features

GetEnvironmentVariable

A method that enables you to access the value of an environment variable

Info

Provides quick access to the assembly of Windows Forms. You can get at assembly information such as version number, name, title, copyright information, and more.

IsNetworkDeployed

Returns a Boolean value that indicates whether the application was distributed via the network using the ClickOnce feature. If True, then the application was deployed using ClickOnce — otherwise False.

Log

Enables you to write to your application's Event Log listeners

MinimumSplashScreenDisplayTime

Enables you to set the time for the splash screen

OpenForms

Returns a FormCollection object, which allows access to the properties of the forms currently open

SaveMySettingsOnExit

Provides the capability to save the user's settings upon exiting the application. This method works only for Windows Forms and console applications.

SplashScreen

Enables you to programmatically assign the splash screen for the application

UICulture

Returns the current culture being used by the Resource Manager

While much can be accomplished using the My.Application namespace, for an example of its use, let's focus on the Info property. This property provides access to the information stored in the application's AssemblyInfo.vb file, as well as other details about the class file. In one of your applications, you can create a message box that is displayed using the following code:

MessageBox.Show("Company Name: " & My.Application.Info.CompanyName & _
   vbCrLf & _
   "Description: " & My.Application.Info.Description & vbCrLf & _
   "Directory Path: " & My.Application.Info.DirectoryPath & vbCrLf & _
   "Copyright: " & My.Application.Info.Copyright & vbCrLf & _
   "Trademark: " & My.Application.Info.Trademark & vbCrLf & _
   "Name: " & My.Application.Info.AssemblyName & vbCrLf & _
   "Product Name: " & My.Application.Info.ProductName & vbCrLf & _
   "Title: " & My.Application.Info.Title & vbCrLf & _
   "Version: " & My.Application.Info.Version.ToString())

From this example, it is clear that you can get at quite a bit of information concerning the assembly of the running application. Running this code produces a message box similar to the one shown in Figure 7-10.

Figure 7-10

Figure 7.10. Figure 7-10

Another interesting property to look at from the My.Application namespace is the Log property. This property enables you to work with the log files for your application. For instance, you can easily write to the system's Application Event Log by first changing the application's app.config file to include the following:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
    <system.diagnostics>
        <sources>
            <source name="DefaultSource" switchName="DefaultSwitch">
<listeners>
                    <add name="EventLog"/>
                </listeners>
            </source>
        </sources>
        <switches>
            <add name="DefaultSwitch" value="Information" />
        </switches>
        <sharedListeners>
            <add name="EventLog"
             type="System.Diagnostics.EventLogTraceListener"
             initializeData="EvjenEventWriter" />
        </sharedListeners>
    </system.diagnostics>
</configuration>

Once the configuration file is in place, you can record entries to the Application Event Log, as shown in the following simple example:

Private Sub Form1_Load(ByVal sender As System.Object, _
   ByVal e As System.EventArgs) Handles MyBase.Load

    My.Application.Log.WriteEntry("Entered Form1_Load", _
       TraceEventType.Information, 1)

End Sub

You could also just as easily use the WriteExceptionEntry method in addition to the WriteEntry method. After running this application and looking in the Event Viewer, you will see the event shown in Figure 7-11.

Figure 7-11

Figure 7.11. Figure 7-11

The previous example shows how to write to the Application Event Log when working with the objects that write to the event logs. In addition to the Application Event Log, there is also a Security Event Log and a System Event Log. Note that when using these objects, it is impossible to write to the Security Event Log, and it is only possible to write to the System Event Log if the application does it under either the Local System or the Administrator accounts.

In addition to writing to the Application Event Log, you can just as easily write to a text file. As with writing to the Application Event Log, writing to a text file also means that you need to make changes to the app.config file:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
    <system.diagnostics>
        <sources>
            <source name="DefaultSource" switchName="DefaultSwitch">
                <listeners>
                    <add name="EventLog"/>
                    <add name="FileLog" />
                </listeners>
            </source>
        </sources>
        <switches>
            <add name="DefaultSwitch" value="Information" />
        </switches>
        <sharedListeners>
            <add name="EventLog"
             type="System.Diagnostics.EventLogTraceListener"
             initializeData="EvjenEventWriter" />
            <add name="FileLog"
             type="Microsoft.VisualBasic.Logging.FileLogTraceListener,
             Microsoft.VisualBasic, Version=8.0.0.0, Culture=neutral,
             PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL"
             initializeData="FileLogWriter"/>
        </sharedListeners>
    </system.diagnostics>
</configuration>

Now with this app.config file in place, you simply need to run the same WriteEntry method as before. This time, however, in addition to writing to the Application Event Log, the information is also written to a new text file. You can find the text file at C:Documents and Settings[username]Application Data[AssemblyCompany][AssemblyProduct][Version]. For instance, in my example, the log file was found at C:Documents and SettingsAdministratorApplication DataWroxLog Writer1.2.0.0. In the .log file found, you will see a line such as the following:

DefaultSource     Information     1     Entered Form1_Load

By default, it is separated by tabs, but you can change the delimiter yourself by adding a delimiter attribute to the FileLog section in the app.config file:

<add name="FileLog"
 type="Microsoft.VisualBasic.Logging.FileLogTraceListener,
 Microsoft.VisualBasic, Version=8.0.0.0, Culture=neutral,
 PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL"
 initializeData="FileLogWriter" delimiter=";" />

In addition to writing to Event Logs and text files, you can also write to XML files, console applications, and more.

My.Computer

The My.Computer namespace can be used to work with the parameters and details of the computer in which the application is running. The following table details the objects contained in this namespace:

Property

Description

Audio

This object enables you to work with audio files from your application. This includes starting, stopping, and looping audio files.

Clipboard

This object enables you to read and write to the clipboard.

Clock

This enables access to the system clock to get at GMT and the local time of the computer running the application. You can also get at the tick count, which is the number of milliseconds that have elapsed since the computer was started.

FileSystem

This object provides a large collection of properties and methods that enable programmatic access to drives, folders, and files. This includes the ability to read, write, and delete items in the file system.

Info

This provides access to the computer's details, such as amount of memory, the operating system type, which assemblies are loaded, and the name of the computer itself.

Keyboard

This object provides information about which keyboard keys are pressed by the end user. Also included is a single method, SendKeys, which enables you to send the pressed keys to the active form.

Mouse

This provides a handful of properties that enable detection of the type of mouse installed, including details such as whether the left and right mouse buttons have been swapped, whether a mouse wheel exists, and how much to scroll when the user uses the wheel.

Name

This is a read-only property that provides access to the name of the computer.

Network

This object provides a single property and some methods that enable you to interact with the network to which the computer on which the application is running is connected. With this object, you can use the IsAvailable property to first verify that the computer is connected to a network. If so, then the Network object enables you to upload or download files, and ping the network.

Ports

This object can provide notification when ports are available, as well as allow access to the ports.

Registry

This object provides programmatic access to the registry and the registry settings. Using the Registry object, you can determine whether keys exist, determine values, change values, and delete keys.

Screen

This provides the capability to work with one or more screens that may be attached to the computer.

There is a lot to the My.Computer namespace, and it is impossible to cover all or even most of it. For an example that uses this namespace, we'll take a look at the FileSystem property. The FileSystem property enables you to easily and logically access drives, directories, and files on the computer.

To illustrate the use of this property, first create a Windows Form with a DataGridView with a single column and a Button control. It should appear as shown in Figure 7-12.

Figure 7-12

Figure 7.12. Figure 7-12

This little application will look in the user's My Music folder and list all of the .mp3 files found therein. Once listed, the user of the application will be able to select one of the listed files; and after pressing the Play button, the file will be launched and played inside Microsoft's Windows Media Player.

The first step after getting the controls on the form in place is to make a reference to the Windows Media Player DLL. You can find this on the COM tab, and the location of the DLL is C:WINDOWSSystem32wmp.dll. This provides you with an object called WMPLib in the References folder of your solution.

You might be wondering why you would make a reference to a COM object in order to play a .wma file from your application, instead of using the My.Computer.Audio namespace that is provided to you. The Audio property only allows for the playing of .wav files, because to play .wma, .mp3, and similar files, users must have the proper codecs on their machine. These codecs are not part of the Windows OS, but are part of Windows Media Player.

Now that the reference to the wmp.dll is in place, let's put some code in the Form1_Load event:

Private Sub Form1_Load(ByVal sender As System.Object, _
    ByVal e As System.EventArgs) Handles MyBase.Load

For Each MusicFile As String _
In My.Computer.FileSystem.GetFiles _
           (My.Computer.FileSystem.SpecialDirectories.MyMusic, _
           FileIO.SearchOption.SearchAllSubDirectories, "*.wma*")
             Dim MusicFileInfo As System.IO.FileInfo = _
                My.Computer.FileSystem.GetFileInfo(MusicFile.ToString())
             Me.DataGridView1.Rows.Add(MusicFileInfo.Directory.Parent.Name & _
                "" & MusicFileInfo.Directory.Name & "" & MusicFileInfo.Name)
        Next
End Sub

In this example, the My.Computer.FileSystem.GetFiles method points to the My Music folder through the use of the SpecialDirectories property. This property enables logical and easy access to folders such as Desktop, My Documents, My Pictures, Programs, and more. Though it is possible to use just this first parameter with the GetFiles method, this example makes further definitions. The second parameter defines the recurse value — which specifies whether the subfolders should be perused as well. In this case, the SearchOption enumeration is set to SearchAllSubDirectories. The last parameter defines the wildcard that should be used in searching for elements. In this case, the value of the wildcard is *.wma, which instructs the GetFile method to get only the files that are of type .wma. You could just as easily set it to *.mp3 or even just *.* to get anything contained within the folders. After it is retrieved with the GetFile method, the file is then placed inside the DataGridView control, again using the My.Computer.FileSystem namespace to define the value of the item placed within the row.

After the Form1_Load event is in place, the last event to construct is the Button1_Click event:

Private Sub Button1_Click(ByVal sender As System.Object, _
   ByVal e As System.EventArgs) Handles Button1.Click
      Dim MediaPlayer As New WMPLib.WindowsMediaPlayer
      MediaPlayer.openPlayer(My.Computer.FileSystem.SpecialDirectories.MyMusic & _
         "" & DataGridView1.SelectedCells.Item(0).Value)
End Sub

From this example, you can see that it is pretty simple to play one of the provided .wma files. It is as simple as creating an instance of the WMPLib.WindowsMediaPlayer object and using the openPlayer method, which takes as a parameter the location of the file to play. In this case, you are again using the SpecialDirectories property. The nice thing about using this property is that whereas it could be more difficult to find the user's My Music folder due to the username changing the actual location of the files that the application is looking for, using the My namespace enables it to figure out the exact location of the items. When built and run, the application provides a list of available music files, enabling you to easily select one for playing in the Media Player. This is illustrated in Figure 7-13.

Though it would be really cool if it were possible to play these types of files using the Audio property from the My.Computer namespace, it is still possible to use the My.Computer.Audio namespace for playing .wav files and system sounds.

To play a system sound, use the following construct:

My.Computer.Audio.PlaySystemSound(SystemSounds.Beep)

The system sounds in the SystemSounds enumeration include Asterisk, Beep, Exclamation, Hand, and Question.

Figure 7-13

Figure 7.13. Figure 7-13

My.Forms Namespace

The My.Forms namespace provides a quick and logical way to access the properties and methods of the forms contained within your solution. For instance, to get at the first form in your solution (assuming that it's named Form1), use the following namespace construct:

My.Form.Form1

To get at other forms, you simply change the namespace so that the name of the form you are trying to access follows the Form keyword in the namespace construction.

My.Resources

The My.Resources namespace is a very easy way to get at the resources stored in your application. If you open the MyResources.resx file from the My Projects folder in your solution, you can easily create as many resources as you wish. For example, you could create a single String resource titled MyResourceString and give it a value of St. Louis Rams.

To access the resources that you create, use the simple reference shown here:

My.Resources.MyResourceString.ToString()

Using IntelliSense, all of your created resources will appear after you type the period after the My.Resources string.

My.User

The My.User namespace enables you to work with the IPrincipal interface. You can use the My.User namespace to determine whether the user is authenticated or not, the user's name, and more. For instance, if you have a login form in your application, you could allow access to a particular form with code similar to the following:

If (Not My.User.IsInRole("Administrators")) Then
   ' Code here
End If

You can also just as easily get the user's name with the following:

My.User.Name

In addition, you can check whether the user is authenticated:

If My.User.IsAuthenticated Then
   ' Code here
End If

My.WebServices

When not using the My.WebServices namespace, you access your Web services references in a lengthier manner. The first step in either case is to make a Web reference to some remote XML Web Service in your solution. These references will then appear in the Web References folder in the Solution Explorer in Visual Studio 2008. Before the introduction of the My namespace, you would have accessed the values that the Web reference exposed in the following manner:

Dim ws As New ReutersStocks.GetStockDetails
Label1.Text = ws.GetLatestPrice.ToString()

This works, but now with the My namespace, you can use the following construct:

Label1.Text = My.WebServices.GetStockDetails.GetLatestPrice.ToString()

Extending the My Namespace

You are not limited to only what the My namespace provides. Just as you can with other namespaces, you can extend this namespace until your heart is content. To show an example of extending the My namespace so that it includes your own functions and properties, in your Windows Forms application, create a new module called CompanyExtensions.vb.

The code for the entire module and the associated class is presented here:

Namespace My
    <HideModuleName()> _
    Module CompanyOperations
Private _CompanyExtensions As New CompanyExtensions

        Friend Property CompanyExtensions() As CompanyExtensions
            Get
                Return _CompanyExtensions
            End Get
            Set(ByVal value As CompanyExtensions)
                _CompanyExtensions = value
            End Set
        End Property
    End Module
End Namespace

Public Class CompanyExtensions
    Public ReadOnly Property CompanyDateTime() As DateTime
        Get
            Return DateTime.Now()
        End Get
    End Property
End Class

From this example, you can see that the module CompanyOperations is wrapped inside the My namespace. From there, a single property is exposed — CompanyExtensions. The class, CompanyExtensions, is a reference to the class found directly below in the same file. This class, CompanyExtensions, exposes a single ReadOnly Property called CompanyDateTime.

With this in place, build your application, and you are now ready to see the new expanded My namespace in action. From your Windows Form application's Page_Load event, add the following code snippet:

MessageBox.Show(My.CompanyExtensions.CompanyDateTime)

From the My namespace, you will now find the CompanyExtensions class directly in the IntelliSense, as presented in Figure 7-14

Figure 7-14

Figure 7.14. Figure 7-14

The name of the module CompanyOperations doesn't also appear in the list off My because the <HideModuleName()> attribute precedes the opening module statement. This attribute signifies that you don't want the module name exposed out to the My namespace.

The preceding example shows how to create your own sections within the My namespace, but you can also extend the sections that are already present (for example, Computer, User, etc.). Extending the My namespace is simply a matter of creating a partial class and extending it with the feature sets that you want to appear in the overall My namespace. An example of such an extension is presented in the following code sample:

Namespace My
    Partial Class MyComputer
        Public ReadOnly Property Hostname() As String
            Get
                Dim iphostentry As System.Net.IPHostEntry = _
                   System.Net.Dns.GetHostEntry(String.Empty)

                Return iphostentry.HostName.ToString()
            End Get
        End Property
    End Class
End Namespace

From this, you can see that this code is simply extending upon the already present MyComputer class:

Partial Class MyComputer

End Class

This extension exposes a single ReadOnly Property called Hostname that returns the local user's hostname. After compiling or utilizing this class in your project, you will find the Hostname property available to you within the My.Computer namespace, as shown in Figure 7-15.

Figure 7-15

Figure 7.15. Figure 7-15

Summary

The introduction of namespaces with the .NET Framework provides a powerful tool that helps to abstract logical capabilities from their physical implementation. While there are differences in the syntax of referencing objects from a namespace and referencing the same object from a COM-style component implementation, there are several similarities. After demonstrating the hierarchical structure of namespaces, this chapter covered the following:

  • Why namespace hierarchies are not related to class hierarchies

  • How to review and add references to a project

  • How to import and alias namespaces at the module level

  • How to create custom namespaces

  • How to use the My namespace

Namespaces play an important role in enterprise software development. They enable you to separate the implementation of related functional objects while retaining the ability to group these objects, which improves the overall maintainability of your code. Anyone who has ever worked on a large project has experienced situations in which a fix to a component was delayed because of the potential impact on other components in the same project. Regardless of the logical separation of components in the same project, developers who take part in the development process worry about testing. With separate implementations for related components, it is not only possible to alleviate this concern, but also easier than ever before for a team of developers to work on different parts of the same project.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset