Q I'm just about to begin development of my first .NET Windows® Forms application. What are the guidelines I should follow to make my application scalable and efficient?
A There are several best practices you can follow. Many of the guidelines apply to most development while others are specific to Windows Forms because of the technologies available. Here are some of the most important ones. Adopt a naming convention You should decide on the naming conventions you are going to use before you begin your project. The Microsoft® .NET Framework contains a number of suggestions on naming conventions. For instance, you can find Visual Basic® naming convention suggestions at this location in the docs:
Some folks believe that these guidelines are the only ones to use, but I disagree. First, it is far more important to decide on and adhere to a set of naming conventions than to limit yourself to a particular one. Second, it is important to think through the impact of the naming conventions you use. For instance, I still like to prefix control names with the type of control because I can type "txt" in the code editor, press Ctrl+Space, and get a list of all the textbox controls on a form, for example. As a result, the naming conventions have an impact on your programming efficiency, and more importantly, on the ability to maintain your code. Use n-tier concepts Two key techniques in n-tier development, coupling and cohesion, influence code reuse. Coupling defines how tightly a particular piece of code is tied to another piece of code, and the rule of thumb is that it should be loosely coupled. For instance, if you have a control named txtCustomer on a form, you should never have a direct reference to txtCustomer in a block of code that provides any type of business or generic functionality. In other words, the code should not be tied directly to that textbox; instead it should be passed the value to the textbox or a reference to the textbox. It is recommended that each block of code be highly specific. For instance, instead of having a block of code named UpdateCustomerOrderAndProducts, you should have functions named UpdateCustomer, UpdateOrder, and UpdateProducts. This concept is well illustrated in the .NET Framework; in many areas (such as ADO.NET), functions were factored out to make each as task-specific as possible. For example, the Command class has methods named ExecuteNonQuery and ExecuteScalar instead of simply having an Execute method which takes parameters to direct the method's behavior. Both of these concepts lead to an n-tier design in which the application is broken apart into layers. Visual Studio® .NET and the .NET Framework make this design much easier to implement because they make it simpler to create and implement classes, component design surfaces, and so forth. Use inheritance for extensibility and maintainability Windows Forms provides a great opportunity to build an extensible framework for your applications. For instance, you can easily customize or extend a control by deriving a new control from an existing one, like so:
Then you can tweak the behavior of the control to meet your needs. For example, I created a control based on the textbox, then added some event code to monitor the keystrokes. The code in Figure 1 shows all that was necessary to modify this control to handle only numeric data. You could create similar controls to handle customer names, customer numbers, or generic data types such as government issued IDs (Social Security numbers in the United States, for instance). Most input controls have a validation event which you can take advantage of. You can find out more about these events at the following location in the MSDN® help:
And don't forget about form inheritance. With form inheritance, you create a base form that has items such as logos, common buttons, menus, and so on, and any new forms simply inherit from this form. This kind of inheritance makes creating and maintaining applications easy and it minimizes the amount of code you must write. However, also make sure to minimize the amount of code in the UI. For instance, a button click event should never have more than a few lines of code in it. If the code gets too complex or handles some type of generic function, put it into a function in a module. Then you have a logically constructed architecture that provides a high level of abstraction and code reusability. You should also invest the time in creating a component framework from which your classes can be built. This rich architecture will make your derived classes much more elegant. Limit resource glut Consider the number of resources used by a form. For instance, instead of having a single form with hundreds of controls, try to narrow the functionality of the form. You can break the form into parts and limit the controls on each form. This is nothing new—you always have to manage resources in any type of app. You can also add and remove controls dynamically so the need to have a large number of static controls is reduced. Write applications to update automatically You can save your organization a tremendous amount of effort by creating auto- updateable applications. Windows Forms allows you to easily write an application that pulls itself down from a Web server and automatically checks for updates. The following short example shows the key components of this process:
Dim formAsm As [Assembly] = _
Dim formtype As Type = formAsm.GetType("FirstForm.FirstForm")
Dim FormObj As Object
FormObj = Activator.CreateInstance(formtype)
Dim Form1 As Form = CType(FormObj, Form)
As you can see, the LoadFrom method takes a URL that points to your DLL to download it automatically. How should you build your application to use this technique? One of the keys is to follow the coupling and cohesion guidelines I discussed earlier. Auto-downloading applications should be built in small pieces. That is, you break the forms up into small chunks and package them in assemblies, making them easy to manage and facilitate faster downloading of updates. You can also place the application components in a Web farm. This gives you the advantage of providing a fault-tolerant distribution mechanism for your application. You could even send the starting point of the application (usually an .exe) to the user in e-mail or post it on a Web site and allow users to access it by clicking a page. I prefer to send the application to the users via a Windows Installer (.msi) file that not only installs the application but also places shortcuts on the Programs menu, desktop, or other location. An MSI installation can also provide other services such as installing files into preferred locations, setting up a local database, placing items into a user's Favorites folder, and so on. The installer even provides an automatic uninstall option. Think stateless Because of the stateless nature of many network apps, you should not build your application to depend upon the state of an instance of a component. You should adhere to this concept even in Windows-based applications, and even if you are not using COM+, because doing so will make your application scalable and flexible. If an application does not depend upon state, the components of that application can usually be moved to provide for scaling and distribution. And, of course, they're easier to debug and maintain. Use a variety of error handling Your application should also have several types of error handling and logging support. Use try-catch error handling throughout to protect your code. This will allow your application to handle errors elegantly without presenting cryptic error messages. You can also rely on features such as rich custom performance monitoring support and custom event logs to allow system managers to monitor your application while it's running. You can also place tracing code in your application to record important features while the application runs. Make fewer calls across processes Consider the transports used to move data between the client and application servers. You can use .NET Remoting or Web Services to handle this task. Remoting using TCP/IP is probably the best approach for intranet applications but, whatever the protocol, always remember to move data in chunks between tiers or clients to minimize the number of calls across processes or the network. Reducing the number of calls is critical in the creation of high-performance applications. Test, test, test Test everything thoroughly, and do unit testing of the various components that go into the application. Visual Studio .NET includes Application Center Test for Web applications. What does this have to do with Windows-based applications? If you build an n-tier application, you can easily test the components by creating a simple Web interface to them. Then you can run a test on the components under a heavy load and monitor the performance. In addition, the Visual Studio Analyzer is a good choice for testing the performance of your application.