Northwind Pocket Delivery: Transportation for Windows Mobile-based Pocket PCs

 

Christian Forsberg
business anyplace

March 2006

Applies to:
   Windows Mobile 2003 software for Pocket PCs
   Microsoft Visual Studio .NET 2003
   Microsoft .NET Compact Framework 1.0

Summary: Learn about mobile transportation and how to design and develop solutions by using Visual Studio .NET and the .NET Compact Framework for Pocket PCs based on Windows Mobile 2003 software. The source code in this article implements a server Web application, an XML Web service, a database, and a Pocket PC client. (53 printed pages)

Download Northwind_Pocket_Delivery_Transport_WM2k3_PPC.msi from the Microsoft Download Center.

Contents

Introduction
Northwind Traders's Delivery Business Process
Application Design
Notification Infrastructure
MapPoint Web Service Integration
Northwind Delivery Web Application Server Walkthrough
Northwind Pocket Delivery Application Client Walkthrough
Code Walkthrough
Conclusion

Introduction

This article continues on the following articles:

The first article describes key elements in how to develop mobile field service applications, and the second focuses more on developing these applications for Smartphones. The third and fourth articles focus on developing mobile field sales applications. The fifth and sixth articles cover the design and implementation of a mobile logistics solution.

This article is about designing and developing a mobile delivery application based on the .NET Framework, the Windows Mobile platform for Pocket PC, and the .NET Compact Framework. The sample solution described in this article addresses the needs of the fictitious company Northwind Traders from a transportation process and field delivery point of view. From a technology point of view, the sample demonstrates how to push notifications to the device by using plain sockets and the notification API, how to integrate with the MapPoint Web Service, how to do simple integration with native Pocket PC functionality such as making a call on a Pocket PC Phone Edition, creating (and sending) an e-mail message, and more.

Northwind Traders's Delivery Business Process

As described in previous articles, the customers of Northwind Traders have vending machines that sell the various products from Northwind Traders. In a business like this, keeping a healthy warehouse is critical because missing stock items will probably affect sales instantly. Therefore, Northwind Traders is not only focusing on supplying its customers with the needed products on time, but the company is also offering customers the ability to exchange products with each other. This innovative service initiative means that customers that have too many items in stock of a certain product could offer that product to other customers. In addition to managing this exchange of products between customers, Northwind Traders is offering a delivery service that will transport products to those customers. Such a business process includes the following steps:

  1. Create a delivery and assign it to a driver.
  2. Notify the driver of his or her new assignments.
  3. Plan the routes for pick up and drop off.
  4. Pick up a package.
  5. Drop off the package.
  6. Report the driver's status.

That business process can be illustrated as shown in Figure 1.

Figure 1. Delivery business process

As shown in Figure 1, the main steps in the business process involve a number of activities that work as main requirements for the new solution. Assuming you have done a good job of defining the new process, the next step is to look at the design of the solution.

Application Design

The article Northwind Pocket Service: Field Service for Windows Mobile-based Pocket PCs provides a good introduction to the architectural work in a mobile solution. The article Northwind Pocket Inventory: Logistics for Windows Mobile-based Pocket PCs includes a description of the most important deliverables (artifacts) in the application design, and the design really begins with the definition of the use cases. Figure 2 shows the use cases for the sample application.

Figure 2. Use case model

Another very important artifact to create early in the design process is the dialog (form) model and sample dialogs. Figure 3 illustrates the dialog model for the sample application.

Figure 3. Dialog model

The dialog model gives an overview of the dialogs included in the application with the navigation between these dialogs. Note that the form for the list of deliveries (plan, pick up, and drop off) is used in the main menu commands Plan, Pick Up, and Drop Off. The application implements it as a single form, but the functionality changes somewhat depending on what role the form has. The same is true for the delivery (plan pick up, plan drop off, pick up, and drop off) form.

Figure 4 shows some sample dialogs.

Figure 4. Sample dialogs

The sample dialogs are drawn in a general drawing tool (in this case, Microsoft PowerPoint), and it is a clear recommendation that someone knowledgeable in user interface design should be involved in creating dialogs like this. The early visualization of the application dialogs gives the users and other stakeholders an opportunity to understand how the application will look (and work), and changes are very easy to make at this stage.

In the Northwind_Pocket_Delivery_Transport_WM2k3_PPC.msi, you can find all the preceding figures in a PowerPoint presentation that you can reuse when creating your own diagrams.

Notification Infrastructure

An interesting part of the solution concerns the second business process step: notifying the driver. After the creation of a new delivery in the back office, the driver who has been assigned the new delivery should be instantly notified. Figure 5 shows an overview of a notification architecture (in online form).

Figure 5. Online notification architecture

The online notification architecture model shown in Figure 5 can be used when the clients are assumed to be online—not necessarily using their connections, but available on the network. A technical infrastructure solution for this model is to use General Packet Radio Services (GPRS), which allows the clients to be connected. As long as the clients are not using the connection, there are no communication costs generated. The technical solution for this model is that each client first notifies the server how it can be reached (its current IP address reported through an XML Web service; see the arrow marked with the number 1 in Figure 5). When a new delivery is created in the back-office Web application (and saved in the database), a notification is instantly sent to the client (by means of TCP/IP sockets; see the arrow marked with the number 2 in Figure 5). This is the model used in this article's sample application.

For scenarios where client connectivity cannot be assumed, a more solid model is required to queue the notification requests and to send the requests to the client when it is online. Figure 6 shows such a notification architecture.

Figure 6. Queued notification architecture

The technical solution for the queued notification architecture shown in Figure 6 is identical in regard to the clients notifying the server of their identity. The main difference is the introduction of a continuously running application (usually implemented as a Windows service) that is trigged by new deliveries created (by the back-office Web application), and that distributes the notifications when the clients are online.

When the clients are connected through a Global System for Mobile Communications (GSM) cellular telephone network, the queued notification architecture can also be implemented through Short Message Service (SMS). When SMS messages sent to a client (subscriber) are queued, they are sent the next time the client is available on the network. A critical aspect to consider with this solution is the cost of the messages, and this technology is not totally reliable. There are many technical solutions for sending the SMS messages from the server. For a solution on how to receive SMS messages on a Pocket PC by using managed code, see Maarten Struys's article Receiving SMS Messages Inside a Managed Application.

MapPoint Web Service Integration

Throughout the delivery business process, the application can assist the driver with geographical information to ease both the planning and the actual delivery. Although there are many technical solutions available for providing the driver with both maps and driving directions, the factor that will make the application truly usable is how well the geographical solution is integrated with the rest of the application. As mentioned in previous articles, XML Web services are excellent ways to integrate applications. With that in mind, the MapPoint Web Service is an interesting solution to consider.

In the delivery business process, there are basically two functions that need geographical information. The first is to show a map of a specific delivery location (pick up or drop off). The use of the MapPoint Web Service can be illustrated as shown in Figure 7.

Figure 7. Get map for location by using MapPoint Web Service

The sample application uses two services of the MapPoint Web Service to get a map of a location. First (illustrated by the arrow marked with the number 1 in Figure 7), an address is found by means of the FindAddress Web method of the find subservice. Then, the map of that address is retrieved by means of the render service's GetMap Web method.

The second use of the MapPoint Web Service is to find a route between two locations, as shown in Figure 8.

Figure 8. Get route between locations by using MapPoint Web Service

The same functionality of the MapPoint Web Service for finding an address and for retrieving maps is also used here. But to calculate the actual route, the sample uses the CalculateSimpleRoute Web method of the route service.

The MapPoint Web Service includes much more functionality related to geographical information. For more details, see the MapPoint Web Service.

Now, you can walk through the completed sample application.

Northwind Delivery Web Application Server Walkthrough

The back-office staff can use a Web application to create new delivery assignments. This Web application is an ASP.NET application written with Microsoft Visual Studio .NET 2003 in C# with data in SQL Server 2000.

The application shows how to support the initial steps in a delivery business process. Because the focus of this article is the mobile solution, this article will not describe how to support the initial steps with regard to source code. However, for completeness, the walkthrough will give an understanding of how this delivery business process step is implemented. If desired, you can examine the source code in this article's sample code.

First Page

The first page of the Web application shows a list of the deliveries and their respective statuses. As shown in Figure 9, the deliveries all have the status Open, which means that they have not yet been delivered.

Figure 9. First page of the Web application. Click the thumbnail for a larger image.

When you click the delivery number of the first delivery, the delivery details appear, as shown in Figure 10. The delivery details include information about the locations to pick up and drop off, in addition to the products to be delivered (Product Name and Quantity).

Figure 10. Delivery details. Click the thumbnail for a larger image.

Note that the Completed date in Figure 10 is not set until the delivery is completed.

Creation of a New Delivery

On the first page, if you click the Add new delivery link, you will start the wizard to create a new delivery. The first step is to select a customer. Type part of the customer name, and when you click Find, the search results appear (as shown in Figure 11).

Figure 11. Select a customer. Click the thumbnail for a larger image.

After you select one of the customers (in this walkthrough, White Clover Markets), select the delivery locations on the next wizard page, as shown in Figure 12. You can select the date that the delivery is required and what driver is assigned to do the delivery. You also enter the locations to pick up and drop off the packages by either typing the information manually or selecting a customer. Click Next when you are finished.

Figure 12. Enter delivery and location information. Click the thumbnail for a larger image.

On the next wizard page, you can add the delivery details (information about the products), as shown in Figure 13. To create a delivery detail, you select a product in the list, and then enter the quantity. When you click New, the delivery detail is added to the list. The Delete link on each row removes the respective delivery detail.

Figure 13. Add product information with quantity. Click the thumbnail for a larger image.

When you click Finish, the application attempts to send a notification (for details, see the earlier "Notification Infrastructure" section) of the new delivery to the assigned driver. If the notification is successfully sent, the result appears as shown in Figure 14.

Figure 14. Notification result. Click the thumbnail for a larger image.

When the notification reaches the client, it appears to the user (driver) as shown in Figure 15.

Figure 15. A notification shown on Pocket PC. Click the thumbnail for a larger image.

The delivery is ready to be transferred to the user (driver), and that occurs during the next synchronization. Clicking Home will return you to the first page.

Next, you will walk through the client sample application.

Northwind Pocket Delivery Application Client Walkthrough

The example client scenario is a Pocket PC application written with Microsoft Visual Studio .NET 2003 in C# and targets the Microsoft .NET Compact Framework 1.0.

The application shows how to support the delivery business process by using a Pocket PC. The design choices align to the process as much as possible and also maximize efficiency for the driver. Some of the design choices will be commented during the walkthrough of the application. Also note that this article explores parts of the code after it describes the application's user interface design.

When you start the application, the first screen is the Main menu screen, as shown in Figure 16. The main steps in this process are to get the delivery assignments, plan these assignments, pick up the packages, drop off the packages, and report the status of each delivery. A ListView control has been used to present the main menu options as icons that can be tapped.

Figure 16. Main menu

All menu options are also available in Menu (in the lower-left corner of the screen). Using a ListView control like this makes it possible to switch modes for the menu. With very small modifications, the menu (now with large icons) can appear as small icons, a list with one column, or even a list with details (more columns that may contain a longer description for each option).

Synchronization

The first step in the delivery process is to receive new delivery assignments by synchronizing with the server. From the Main menu screen, tap Sync.

You use the Synchronize screen, shown in Figure 17, to synchronize data with the server. New assignments are transferred from the server, and completed deliveries (drop offs) are transferred back to the server. Synchronizing with the server is implemented as a two-step wizard, and you are first required to enter the user name and password for this process.

Figure 17. Synchronization

After you enter the user name and password, you initiate the synchronization by tapping the Start button. During the synchronization, the progress is reported as shown in Figure 18.

Figure 18. Synchronization completed

When the synchronization is complete, tap the Close button to go back to the Main menu screen.

Plan

The next step in the delivery process is to plan the assigned deliveries. From the Main menu screen, tap Plan.

You can use the Plan screen, shown in Figure 19, to search for deliveries that you want to plan. You can enter search criteria like delivery number, address, and name of the customer. When you tap the Find button, the matching deliveries appear in the list. Note that the list includes both pick ups and drop offs, as indicated by their respective symbols (blue for pick up and green for drop off). To show details for a pick up or drop off, tap and hold it in the list until a pop-up menu appears with a View command, as shown in Figure 19.

Figure 19. Search deliveries to plan

When you tap the View command on a pick up item, the first page of the Plan Pick Up wizard appears, as shown in Figure 20. This first page includes the delivery number, address, customer, and contact person (with contact information such as phone number and e-mail address).

Figure 20. First screen of Plan Pick Up wizard

The application implements contact information as links that can be tapped. When you tap the phone number, and the application is running on a Pocket PC Phone Edition, you can dial the number directly after the confirmation shown in Figure 21.

Figure 21. Dial contact phone number

When you tap the e-mail address, the application implements a new e-mail message, as shown in Figure 22.

Figure 22. Send e-mail to contact

When you tap the customer name shown earlier in Figure 20, the customer information screen appears, as shown in Figure 23.

Figure 23. Customer information

The phone number and e-mail address are links that you can tap to get the same result shown earlier in Figure 21 and Figure 22.

When you tap the Next button shown earlier in Figure 20, the next page in the wizard appears. It includes information about the products to deliver, as shown in Figure 24.

Figure 24. Products to deliver

To show details for a product, tap and hold it in the list until a pop-up menu appears, and then tap the View command. Then, the product information appears, as shown in Figure 25.

Figure 25. Products to pick up

Note that you can update the quantity to deliver. When you tap the Next button on the screen shown earlier in Figure 24, the final page in the wizard appears. It includes more information about the delivery, as shown in Figure 26.

Figure 26. More pick up information

Here, you can add a comment, and you can update the name of the contact. When you tap the Finish button, you go back to the Plan screen shown earlier in Figure 19. On this screen, you can tap and hold a drop off to plan, as shown in Figure 27.

Figure 27. Plan deliveries

When you tap the View command on a drop off item, the first page of the Plan Drop Off wizard appears, as shown in Figure 28. Just as for pick ups, this page includes delivery number, address, customer, contact person, phone number, and e-mail address. The application implements the latter three as links, as shown earlier in Figure 21–23.

Figure 28. Plan drop off

The Next button takes you to the next page in the wizard, which shows the product information, as shown in Figure 29.

Figure 29. Product to drop off

Tapping the Next button again shows the final page of the wizard (more delivery information), as shown in Figure 30.

Figure 30. More information about drop off

After you edit a comment and the contact name, tapping the Finish button takes you back to the Plan screen shown earlier in Figure 19 and Figure 27. On this screen, you can tap and hold any item to show a pop-up menu. On this menu, you can tap Move Up or Move Down to reorder the delivery items to create a route for the assigned deliveries (pick ups and drop offs).

In this process, you can access geographical information. First, you can tap the Show on Map command to get a map of the selected location. Figure 31 shows an example of such a map.

Figure 31. Show location on map

The MapPoint Web Service retrieves a map of the location by using the address of the pick up or drop off. When the map is retrieved, you can use the Zoom In and Zoom Out buttons to get a map that shows more or fewer details. For example, tapping Zoom In twice will result in a more detailed map, as shown in Figure 32.

Figure 32. More detailed map

Tap OK to go back to the Plan screen (shown earlier in Figure 27) again. Then, tap and hold any item, and then tap the Route to Next command to get a route from the selected location to the next in the list, as shown in Figure 33.

Figure 33. First step of route

Both the originating and destination addresses are shown on this screen. You can navigate the route by using the Next and Back buttons, as shown in Figures 34–37.

Figure 34. Navigating a route (the second of ten screens)

Figure 35. Navigating a route (the fourth of ten screens)

Figure 36. Navigating a route (the sixth of ten screens)

Figure 37. Navigating a route (the tenth of ten screens)

Because the maps are retrieved from an XML Web service, this approach provides the driver with detailed geographical information while not consuming a lot of resources on the device. This approach becomes even more powerful as you move on to the actual pick up and drop off of the deliveries.

Pick Up

The next step in the delivery process is the pick-up of the products. On the Main menu screen, tap Pick Up.

You can use the Pick Ups screen to search for pick ups to make. You can enter search criteria like delivery number, address, and name of the customer. When you tap the Find button, the matching pick ups appear in the list. To show details for a pick up, tap and hold it in the list until a pop-up menu appears with a View command, as shown in Figure 38.

Figure 38. Search pick ups

When you tap the View command on a pick up item, the first page of the pick up wizard appears, as shown in Figure 39. This first page includes the delivery number, address, customer, contact person, phone number, and e-mail address.

Figure 39. Pick up information

You can also get directly to this screen if you enter a product number (on the screen shown earlier in Figure 38), and then press the ENTER key on the on-screen keyboard. This technique is how you can emulate a bar-code scanner. If you have a bar-code scanner, it can probably be set up to scan directly into the product number text box, and then (if configured correctly) send an additional press of the ENTER key. If the delivery number is printed as a bar code on the packages, the pick up can be selected by means of a bar-code scanner.

The application implements the phone number, e-mail address, and customer name as links, as shown earlier in Figure 21–23. When you tap the Next button on the screen shown in Figure 39, the next page in the wizard appears. It includes information about the products to pick up, as shown in Figure 40.

Figure 40. Products to pick up

If you need to modify the quantity for each product, you can tap and hold the product in the list. When the pop-up menu appears, you can tap the View command to display the product information, as shown earlier in Figure 25. The main purpose of this step, however, is to mark which products were picked up, and you do this by selecting the check box for each product.

When you tap the Next button on the screen shown in Figure 40, the final page in the wizard appears. It includes more information about the delivery, as shown in Figure 41.

Figure 41. More pick up information

You can update the comment and contact name, and if the pick up is completed, you can select the Completed check box. Tapping the Finish button takes you back to the Pick Ups screen shown earlier in Figure 38.

Drop Off

The next and final step in the delivery process is the actual delivery or drop off of the products picked up earlier. From the Main menu screen, tap Drop Off.

You can use the Drop Offs screen to search for drop offs to make. You can enter search criteria like delivery number, address, and name of the customer. When you tap the Find button, the matching drop offs appear in the list. To show details for a drop off, tap and hold it in the list until a pop-up menu appears with a View command, as shown in Figure 42.

Figure 42. Search drop offs

When you tap the View command on a drop off item, the first page of the drop off wizard appears, as shown in Figure 43. This first page includes the delivery number, address, customer, contact person, phone number, and e-mail address.

Figure 43. Drop off information

You can also get directly to this screen if you enter a product number (on the screen shown earlier in Figure 42), and then press the ENTER key on the on-screen keyboard. Just as for pick ups, this technique is how you can emulate a bar-code scanner; if the delivery number is printed as a bar code on the packages, the drop off can be selected by means of a bar-code scanner.

The application implements the phone number, e-mail address, and customer name as links, as shown earlier in Figure 21–23. When you tap the Next button on the screen shown in Figure 43, the next page in the wizard appears. It includes information about the products to drop off, as shown in Figure 44.

Figure 44. Products to drop off

If you need to modify the quantity for a product, you can tap and hold the product in the list, and then tap View in the pop-up menu that appears. More importantly, the products that are dropped off can be checked off. When you tap the Next button on the screen shown in Figure 44, the final page in the wizard appears. It includes more information about the delivery, as shown in Figure 45.

Figure 45. More drop off information

You can update the comment and contact name, and the contact will also confirm the delivery by making a signature directly in the Signature area. If the drop off is completed, you can select the Completed check box. Tapping the Finish button takes you back to the Drop Offs screen shown earlier in Figure 42.

Customers

After you tap Customers on the Main menu screen, you can search for customers by customer name and contact name, as shown in Figure 46. When you tap the Find button, the application fills the list of customers. Each customer in the list displays a customer name and a contact name.

Figure 46. Customer list

When you tap and hold a customer name in the list, and then select the View command, the customer details appear, as shown in Figure 47.

Figure 47. Customer details

The screen shown in Figure 47 is actually the same as the screen shown earlier in Figure 23, and the functionality is identical.

Options

The Options screen (shown in Figure 48) appears when you tap Options on the Main menu screen.

Figure 48. Options

In the Web Service (URL) box, enter the URL for the XML Web service that handles the synchronization of newly assigned and completed deliveries in addition to reporting the current IP address (for the notification functionality to use). In the Server Login (Username) box, enter the default user name that the application uses when synchronizing. Finally, in the Notification Port box, enter the port number that the application uses to notify the device of a new assignment.

About

All applications should include a screen with the product name, version, copyright, and other legal information. On the Main menu screen, tapping the About option displays this information, as shown in Figure 49.

Figure 49. About the application

This screen can also include a link to a Web page with product and support information.

This completes the walkthrough of the application, but because the application supports globalization (and localization), the following section also shows a few translated screens.

World Ready

When you change the regional settings (tap Start, tap Settings, and then tap Regional Settings) to Portuguese (Brazil) on your Pocket PC and then restart the application, the complete application is translated. The following figures are the Brazilian Portuguese versions of the screens shown in Figures 16, 19, 20, and 33.

Figure 50 shows the translated Main menu screen, and you can see that the form's title bar and the form's controls (like the Menu principal label) are translated—as are the menu icon labels. And even though it's not shown, the menu (Menu) is also translated.

Figure 50. Translated main menu

Figure 51 shows the translated Plan screen. The form title and form controls (labels and buttons) are translated, just like the column titles in the list. Also, the Edit and pop-up menus and their respective menu commands are translated.

Figure 51. Translated Plan screen (search deliveries)

In Figure 52, you can see the translated information about the pick up.

Figure 52. Translated pick up information

Finally, Figure 53 shows how the Route screen looks after being translated.

Figure 53. Translated route screen

This concludes the walkthrough of the client application.

Code Walkthrough

The previous section provided an example client scenario for the Pocket Delivery application, and now it's time to look at the source code of that sample. The article Northwind Pocket Service: Field Service for Windows Mobile–based Pocket PCs covers the general parts of the code, so this article will focus on the unique aspects of the sample.

Notification Infrastructure

This code walkthrough starts by examining the way that the application implements the notification architecture. The logic starts on the server and the Web application for the back-office personnel. When a new delivery is created, the notification is sent to the client (Pocket PC) from an ASP.NET Web form (page) that uses the following code example.

string deliveryID = Request.QueryString.Get("DeliveryID");
string from = Request.QueryString.Get("From");
string to = Request.QueryString.Get("To");

// Get IP address
string ipAddress;
using (SqlConnection cn =
  new SqlConnection(Common.Values.ConnectionString))
{
  cn.Open();
  SqlCommand cmd = cn.CreateCommand();
  cmd.CommandText = "SELECT E.IpAddress FROM Employees E JOIN" +
    " Deliveries D ON E.EmployeeID=D.EmployeeID" +
    " WHERE D.DeliveryID='" + deliveryID + "'";
  ipAddress = cmd.ExecuteScalar().ToString();
}

try
{
  TcpClient tcpClient = new TcpClient();
  tcpClient.Connect(new IPEndPoint(IPAddress.Parse(ipAddress),
    Common.Values.NotificationPort));

  // Send notification
  string message = "You have been assigned a new delivery from" +
    " <font color='#0000FF'>" + from + "</font> to <font" +
    " color='#0000FF'>" + to + "</font>.<br>Please synchronize!";
  Byte[] messageBytes = Encoding.ASCII.GetBytes(message.ToCharArray());
  tcpClient.GetStream().Write(messageBytes, 0, messageBytes.Length);

  // Receive response
  Stream s = tcpClient.GetStream();
  Byte[] buffer = new Byte[250];
  int bytes = s.Read(buffer, 0, buffer.Length);
  string response = Encoding.ASCII.GetString(buffer, 0, bytes);

  if(response == "OK")
    acknowledgementLabel.Text = "Delivery notification sent" +
      " successfully to IP address: " + ipAddress + "!";
  else
    acknowledgementLabel.Text = "Delivery notification sent to IP" +
      " address " + ipAddress + " with unknown response!";
}
catch(Exception)
{
  acknowledgementLabel.Text = "Delivery notification COULD NOT" +
    " be sent to IP address: " + ipAddress + "!";
}

After the page parameters (deliveryID, from, and to) are saved, the IP address of the client is retrieved from the database. The current IP address of the client is updated through the XML Web service (see the following code example for details). When the socket is connected through the IP address and the defined port, the message is constructed from the page parameters and sent to the client through the socket. Note that the message needs to be converted to ASCII before it is sent over the socket. The next step is to retrieve the response from the client, and the notification was sent successfully if the string "OK" is returned. The response appears on the page by means of a label control (acknowledgementLabel). If another response is returned, or if an error occurs, another response appears on the page.

The following code example implements the XML Web service method that receives the current IP address from the client.

[WebMethod]
public void ReportIpAddress(string employeeID, string ipAddress)
{
  // Remove this IP from any other employee, and add to this employee
  using(SqlConnection cn = new SqlConnection(this.connectionString))
  {
    cn.Open();
    SqlCommand cmd = cn.CreateCommand();
    cmd.CommandText = "UPDATE Employees SET IpAddress=NULL" +
      " WHERE IpAddress='" + ipAddress +
      "' AND EmployeeID<>'" + employeeID + "'";
    cmd.ExecuteNonQuery();
    cmd.CommandText = "UPDATE Employees SET IpAddress='" + ipAddress +
      "' WHERE EmployeeID='" + employeeID + "'";
    cmd.ExecuteNonQuery();
  }
}

With the connection created and opened, the IP address supplied as a parameter is removed from any other employee than the one supplied. This technique prevents two clients from being associated with the same IP address because IP addresses can be shared between clients (which often happens when Dynamic Host Configuration Protocol [DHCP] is used for IP address provisioning). Then, the IP address supplied is associated with the employee supplied.

Turning now to the client (Pocket PC) side, the following code example updates the IP address.

WebServices.Delivery deliveryWebService = new WebServices.Delivery();

// Set URL of Web service
deliveryWebService.Url = Common.Values.WebServiceUrl;

// Report current IP address to server
deliveryWebService.ReportIpAddress(Common.Values.EmployeeID,
  Dns.Resolve(Dns.GetHostName()).AddressList[0].ToString());

When the XML Web service proxy instance (deliveryWebService) is created, the URL is set with the value entered on the Options screen (see Figure 48 earlier in this article). To update the server, the employee ID (in a real-world scenario, this is set during installation) and the IP address of the device are sent as parameters to the ReportIpAddress XML Web service method.

A more interesting construct is the code that listens for notifications from the server. The NotifyListener class is implemented as a singleton class that runs the actual listener in a separate thread. The class includes the following private variables.

private Thread listenerThread = null;
private bool listenerThreadActive = false;

To access the native Pocket PC Notification API, you can use the managed wrapper included in the Smart Device Framework from OpenNETCF; the Smart Device Framework is also available with source code. You can use event handlers to act on different selections made in the notification message. In this sample, you only need to show the message and close the notification with a single button, so the event handlers are not needed. The other variables are the background thread (listenerThread) and the flag indicating that the thread is running (listenerThreadActive). When the singleton is created, the following code example runs.

listenerThread = new Thread(new ThreadStart(listener));
listenerThreadActive = true;
listenerThread.Start();

The thread is created and started, and the thread is implemented as follows.

private void listener()
{
  TcpListener tcpListener =
    new TcpListener(Common.Values.NotifyListenerPort);
  tcpListener.Start();

  while(listenerThreadActive) 
  {
    // Look for notification
    if(tcpListener.Pending())
    {
      // Get notification message
      TcpClient tcpClient = tcpListener.AcceptTcpClient();
      Stream s = tcpClient.GetStream();
      Byte[] buffer = new Byte[250];
      int bytes = s.Read(buffer, 0, buffer.Length);
      string message = Encoding.ASCII.GetString(buffer, 0, bytes);

      // Send back confirmation
      string response = "OK";
      Byte[] responseBytes =
        Encoding.ASCII.GetBytes(response.ToCharArray());
      tcpClient.GetStream().Write(responseBytes, 0,
        responseBytes.Length);

      tcpClient.Close();
      showNotification(message);
    }
    Thread.Sleep(1000);
  }
}

A socket listener (tcpListener) is created. This listener checks for incoming messages through its Pending method, which returns true if there is a pending connection request from the server (made by the Connect method). A check is made every second (the thread sleeps for a thousand milliseconds between checks). If there is a pending connection request from the server, the message sent over the socket is retrieved and decoded from ASCII. Then, a confirmation is sent back to the server indicating that the notification message was successfully retrieved. Finally, the message appears as a notification that has the following code.

private void showNotification(string message)
{
  Notification notification = new Notification();
  notification.Caption = GlobalHandler.Translate.Text(
    "Title", "Pocket Delivery");
  notification.Text = "<html><body>" + message +
    "<form method='POST' action=><p align=right><input" +
    " type=button name='cmd:1' value='" +
    GlobalHandler.Translate.Text(
      "NotifyListenerCloseButton", "Close") + "'></p></form>" +
    "</body></html>";
  notification.InitialDuration = 10;
  notification.Visible = true;
}

The notification instance (notification) is created and assigned a title. The message body is defined through a limited form of HTML that allows custom formatting of the message and other user interface elements, such as buttons. In this case, only some simple formatting (such as alignment and font color) occurs, and you can see the result in Figure 15 earlier in this article. The notification is defined to be shown for 10 seconds, and setting the Visible property to true, shows it to the user. Note how the title and button are translated by means of the reusable GlobalHandler class (described in more detail later in this article).

As mentioned in the beginning of this article, this architecture is dependent on the client being online (connected) when the notification is sent from the server. In many cases, the application requires another handling of messages that queue notifications on the server until the client is available. However, implementing such a solution requires changes only on the server side. First, a new status is needed to indicate that that the notification is not yet sent. Then, a constantly running application (probably implemented as a Windows service) checks for new deliveries that are not yet sent. If the client is available, the notification is sent and the status of the delivery is updated. If the client is not available, nothing happens until the next check occurs.

MapPoint Web Service Integration

When you request a map for a location (pick up or drop off), the MapPoint Web Service retrieves first the address and then the map. Please note that to use the MapPoint Web Service, you need an account. If you do not have an account, you can apply for a free 45-day trial account.

You need to insert the account information in the Common class of the sample code. The other values defined for the MapPoint Web Service in the Common singleton (Values) instance are as follows.

public string MapPointUserName = "999999";
public string MapPointPassword = "??????";
public string MapPointDataSourceName = "MapPoint.NA";
public string MapPointCountryRegion = "United States";

When the Map screen (shown earlier in Figure 31) appears, the following code runs.

addressResults = getAddress(address, city, postalCode);
view =
  addressResults.Results[0].FoundLocation.BestMapView.ByHeightWidth;
getMap();

First, the address is retrieved. From that, the best map view is saved. The parameters (address, city, and postalCode) are passed to the form upon creation. Both the address (addressResults) and the view of the location (view) are declared as private class variables as follows.

private FindResults addressResults;
private ViewByHeightWidth view;

The MapPoint Web Service defines these types.

The private method to find the address looks like the following.

private FindResults getAddress(string addressLine,
  string primaryCity, string postalCode)
{
  Address address = new Address();
  address.AddressLine = addressLine;
  address.PrimaryCity = primaryCity;
  address.PostalCode = postalCode;
  address.CountryRegion = Common.Values.MapPointCountryRegion;

  FindAddressSpecification findAddressSpec =
    new FindAddressSpecification();
  findAddressSpec.DataSourceName =
    Common.Values.MapPointDataSourceName;
  findAddressSpec.InputAddress = address;

  FindServiceSoap findService = new FindServiceSoap();
  findService.Credentials = new
    NetworkCredential(Common.Values.MapPointUserName,
    Common.Values.MapPointPassword);

  return findService.FindAddress(findAddressSpec);
}

An address instance (address) is created and initialized with the address information, and then is linked to an instance of a specification for finding an address (findAddressSpec). That specification is then set with the correct data source, and is used in the call (FindAddress method) to the find subservice (FindServiceSoap). Before the call, the valid credentials (account information) are set on the find subservice.

The private method to retrieve the actual map looks like the following.

    private void getMap()
    {
      RenderServiceSoap renderService  = new RenderServiceSoap();
      renderService.Credentials =
        new NetworkCredential(Common.Values.MapPointUserName,
        Common.Values.MapPointPassword);
      renderService.PreAuthenticate = true;

      Pushpin pushpin = new Pushpin();
      pushpin.IconDataSource = "MapPoint.Icons";
      pushpin.IconName = "0";
      pushpin.Label =
        addressResults.Results[0].FoundLocation.Entity.Name;
      pushpin.LatLong = view.CenterPoint;
      pushpin.ReturnsHotArea = true;

      MapSpecification mapSpec  = new MapSpecification();
      mapSpec.DataSourceName = Common.Values.MapPointDataSourceName;
      mapSpec.Views = new ViewByHeightWidth[] { view };
      mapSpec.Pushpins = new Pushpin[] { pushpin };
      mapSpec.Options = new MapOptions();
      mapSpec.Options.Format = new ImageFormat();
      mapSpec.Options.Format.Width = mapPictureBox.Width;
      mapSpec.Options.Format.Height = mapPictureBox.Height;

      MapImage[] mapImages = renderService.GetMap(mapSpec);
      mapPictureBox.Image =
        new Bitmap(new MemoryStream(mapImages[0].MimeData.Bits));
    }

First, a standard push-pin is created to mark the location of the address (which is always at the center of the view), and a map specification (mapSpec) is created based on the view. The push-pin is added to the map specification, and the format of the map is defined as options of the map specification. The map specification is then used to get the map (GetMap method) from the rendering subservice (renderService). The returned map image includes the image bits used to create a bitmap that is assigned directly to the picture box (mapPictureBox).

The buttons to zoom in or out on the map simply change the format of the view and retrieve a new map. The code for the Zoom In button looks like the following.

view.Height /= 2;
view.Width /= 2;
getMap();

The height and width are simply divided by two before the new map is retrieved (as shown earlier in Figure 32).

The code for the Zoom Out button is very similar and looks like the following.

view.Height *= 2;
view.Width *= 2;
getMap();

Here, the height and width are multiplied by two before retrieving a new map.

Note that this implementation requires a new roundtrip for each zoom. If you want to reduce the number of XML Web service calls, you can retrieve several maps in one call by providing the map specification (mapSpec) with a number of views. The retrieval of several maps in one call is actually what happens when the sample code retrieves the maps for a route, as shown below.

Next, you'll look at how the MapPoint Web Service finds the route between two locations. When the Route screen (shown earlier in Figure 33) appears, the following code runs.

FindResults addressResults =
  getAddress(fromAddress, fromCity, fromPostalCode);
LatLong fromLatLong = addressResults.Results[0].FoundLocation.LatLong;
addressResults = getAddress(toAddress, toCity, toPostalCode);
LatLong toLatLong = addressResults.Results[0].FoundLocation.LatLong;
LatLong[] latLongs = new LatLong[] { fromLatLong, toLatLong };
getRoute(latLongs);

Just as when a single map was retrieved in the map form (code shown previously), the address is retrieved. But here, the address is retrieved for both the origin and destination addresses. For each address, the exact position (a LatLong class instance) is inserted into an array that gets the actual route. The parameters (fromAddress, fromCity, fromPostalCode, toAddress, toCity, and toPostalCode) are passed to the form when it is created.

The implementation of the private method to get the address (getAddress) is identical to the one shown for the map form, and the private method to get the route looks like the following.

private void getRoute(LatLong[] latLongs)
{
  NetworkCredential credentials =
    new NetworkCredential(Common.Values.MapPointUserName,
    Common.Values.MapPointPassword);

  RouteServiceSoap routeService = new RouteServiceSoap();
  routeService.Credentials = credentials;
  routeService.PreAuthenticate = true;
  Route route = routeService.CalculateSimpleRoute(latLongs,
    Common.Values.MapPointDataSourceName, SegmentPreference.Quickest);
  int mapCount = route.Itinerary.Segments[0].Directions.Length + 1;
  ViewByHeightWidth[] views = new ViewByHeightWidth[mapCount];
  maps = new Bitmap[mapCount];

  Pushpin[] pushpins = new Pushpin[mapCount];
  for(int i = 0; i < mapCount - 1; i++)
  {
    views[i] =
      route.Itinerary.Segments[0].Directions[i].View.ByHeightWidth;
    pushpins[i] = new Pushpin();
    pushpins[i].IconDataSource = "MapPoint.Icons";
    pushpins[i].IconName = "0";
    pushpins[i].LatLong =
      route.Itinerary.Segments[0].Directions[i].LatLong;
    pushpins[i].ReturnsHotArea = true;
  }
  views[mapCount - 1] = 
    route.Itinerary.Segments[1].Directions[0].View.ByHeightWidth;
  pushpins[mapCount - 1] = new Pushpin();
  pushpins[mapCount - 1].IconDataSource = "MapPoint.Icons";
  pushpins[mapCount - 1].IconName = "1";
  pushpins[mapCount - 1].LatLong =
    route.Itinerary.Segments[1].Directions[0].LatLong;
  pushpins[mapCount - 1].ReturnsHotArea = true;

  MapSpecification mapSpec  = new MapSpecification();
  mapSpec.DataSourceName = Common.Values.MapPointDataSourceName;
  mapSpec.Views = views;
  mapSpec.Pushpins = pushpins;
  mapSpec.Route = route;
  mapSpec.Options = new MapOptions();
  mapSpec.Options.Format = new ImageFormat();
  mapSpec.Options.Format.Width = mapPictureBox.Width;
  mapSpec.Options.Format.Height = mapPictureBox.Height;

  RenderServiceSoap renderService  = new RenderServiceSoap();
  renderService.Credentials = credentials;
  renderService.PreAuthenticate = true;
  MapImage[] mapImages = renderService.GetMap(mapSpec);
  for(int i = 0; i < mapCount; i++)
    maps[i] = new Bitmap(new MemoryStream(mapImages[i].MimeData.Bits));

  // Go to first step
  headingLabel.Text =
    GlobalHandler.Translate.Text("RouteFormheadingLabel", "Route") +
    " 1/" + mapCount.ToString();
  if(mapCount > 0)
    nextButton.Enabled = true;
  backButton.Enabled = false;
  currentStep = 0;
  mapPictureBox.Image = maps[currentStep];
}

Here, the network credentials (credentials) are inserted into a variable to simplify the assignment to the subservice instances. The render subservice (routeService) calculates the actual route (method CalculateSimpleRoute) by using the provided positions of the two addresses (origin and destination). The views (steps) are retrieved from the route, and push-pins are created for each map in the route. A map specification (mapSpec) is set up by means of the views, the push-pins, the route, and the required format of the maps. Just as for the map form, the render subservice (renderService) gets the maps (GetMap method) that are saved as bitmaps in a private class array variable (maps). The first map is then assigned to the picture box (mapPictureBox), and the form is initialized to the first step of the route.

The buttons to navigate the route simply display another map in the private array (maps). The code for the Next button looks like the following.

currentStep += 1;
headingLabel.Text = GlobalHandler.Translate.Text(
  "RouteFormheadingLabel", "Route") + " " +
  ((int)(currentStep + 1)).ToString() + "/" + maps.Length.ToString();
nextButton.Enabled = (currentStep < maps.Length - 1);
backButton.Enabled = (maps.Length > 0);
mapPictureBox.Image = maps[currentStep];

The heading label is updated, the button's visibility is adjusted, and the corresponding map is assigned to the picture box. This way, the route can be navigated forward (as shown earlier in Figure 34–37). The code for the Back button is very similar and looks like the following.

currentStep -= 1;
headingLabel.Text = GlobalHandler.Translate.Text(
  "RouteFormheadingLabel", "Route") + " " +
  ((int)(currentStep + 1)).ToString() + "/" + maps.Length.ToString();
if(maps.Length > 0)
  nextButton.Enabled = true;
backButton.Enabled = (currentStep > 0);
mapPictureBox.Image = maps[currentStep];

This way, the route can be navigated backward as well.

The implementation shows how you can use the MapPoint Web Service to integrate geographical information into an enterprise application. The MapPoint Web Service includes a lot of functionality that you are encouraged to explore. There are many other technical articles on MSDN about using the MapPoint Web Service, and the MapPoint Development Center is a good point to start.

Also, in combination with products like MapPoint Location Server, the MapPoint Web Service can be used to create very advanced solutions. For example, in the sample's server Web application, you can use MapPoint Location Server to include functionality to find the driver locations before assigning a new delivery. If it is a very urgent delivery, the back-office personnel can then select and notify the driver closest to the pick up location.

Initiating Calls and Sending E-Mail

As shown in the sample walkthrough, several items in the forms are implemented as links (similar to those found on Web pages). Examples include the phone number and e-mail addresses of contacts. Because the functionality to make calls and send messages is not available in the .NET Compact Framework 1.0, the integration with the standard Pocket PC functionality occurs through the Pocket PC Web browser (Pocket Internet Explorer). However, the .NET Compact Framework 2.0 has managed support for this functionality.

Note that the following phone number links will work only on a Windows Mobile–based Pocket PC Phone Edition device. When you tap a phone number link, the following line of code runs.

Process.Start("iexplore.exe", "tel:" + phoneLinkLabel.Text);

The Process class is included in the Smart Device Framework from OpenNETCF; the Smart Device Framework is also available with source code. The Start method starts an application with the provided parameter string. Here, Pocket Internet Explorer starts with a phone number link as the parameter. You can achieve the same result by entering the link in the address bar of Pocket Internet Explorer on the Pocket PC. The result is that the Phone Edition device asks (through a notification message) whether you want to call the phone number (as shown earlier in Figure 21).

Something that works on all Windows Mobile–based Pocket PC devices is the following code for an e-mail address link.

Process.Start("iexplore.exe", "mailto:" + emailLinkLabel.Text);

The logic is the same as for the phone number links, and the result here is that the standard Inbox application of the Pocket PC starts (by Pocket Internet Explorer in turn) with a new e-mail message initiated with the provided e-mail address (as shown earlier in Figure 22).

Even if you also provide the subject by using this approach, you do not have much control of the creation of the e-mail message. For example, there is no way to add attachments. To get more control, you can use one of the available options. If you really want to do everything yourself, you can make a managed wrapper for the Messaging API (MAPI). If not, you can choose the commercial wrapper WindowsMobile from InTheHand. The functionality is also available on devices with Windows Mobile 5.0, and managed support is included in the .NET Compact Framework 2.0.

If the users have access to a Simple Mail Transfer Protocol (SMTP) server on the network, you can use an SMTP managed wrapper by Alex Yahknin. You use the wrapper as follows.

SMTP.SmtpDirect.SmtpServer = "smtp.companyname.com";
MailMessage message = new MailMessage();
message.Body = "This is the body test.";
message.From = "someone@microsoft.com";
message.To = "someone@microsoft.com";
message.Subject = "Message with attachment";
MailAttachment attachment = new MailAttachment(@"\Windows\alerts.bmp");
message.Attachments.Add(attachment);
SMTP.SmtpDirect.Send(message)

However, as always, more control means more work. If you want to create the e-mail messages yourself, you will need to implement your own user interface. If you want to provide your users with functionality that they expect (like picking an e-mail address form the Pocket PC Contacts application), you will need to implement that integration as well. If you are using a device with Windows Mobile 5.0 in combination with the .NET Compact Framework 2.0, you have managed support that includes the complete user interface.

Basic Bar-Code Scanner Support

As mentioned about Figure 38 and Figure 42 earlier in this article, you can enter the delivery number and then press the ENTER key to directly make a selection of delivery and go to the delivery details page. The code for the KeyUp event of the delivery number text box (numberTextBox) looks like the following.

private void numberTextBox_KeyUp(object sender,
  System.Windows.Forms.KeyEventArgs e)
{
  if(e.KeyCode == Keys.Return  && formMode != DeliveryFormMode.Plan)
  {
    int deliveryNo = 0;
    try { deliveryNo = Convert.ToInt32(numberTextBox.Text); } 
    catch {}
    if(deliveryNo > 0)
    {
      string deliveryID;
      string deliveryLocationID;
      using(DeliveryHandler deliveryHandler = new DeliveryHandler())
      {
        deliveryID = deliveryHandler.GetIDForNo(deliveryNo);
        deliveryLocationID =
          deliveryHandler.GetLocationIDForIDAndType(deliveryID,
          Convert.ToInt32(formMode) - 1);
      }
      parentForm = true;
      DeliveryForm deliveryForm =
        (DeliveryForm)FormCache.Instance.Load(typeof(DeliveryForm));
      deliveryForm.FormMode = formMode;
      deliveryForm.DeliveryType = Convert.ToInt32(formMode) - 1;
      deliveryForm.DeliveryLocationID = deliveryLocationID;
      FormCache.Instance.Push(typeof(DeliveryForm));
      e.Handled = true;
    }
  }
}

If the key pressed is the ENTER key, and the text box holds an integer value (presumably a delivery number), the delivery identity is retrieved, and the delivery detail form is opened.

This way, any bar-code scanner can be used that supports simulating the keyboard and finishing the scan by adding a final press of the ENTER key. Correctly configured, most bar-code scanners have this capability.

However, most bar-code scanners have even more sophisticated capabilities (even with support for the .NET Compact Framework), like custom controls that are bar-code scanner aware. Such controls increase the control of the scans (with features like detailed exceptions when something goes wrong) but make the support for multiple bar-code scanners more complicated.

Reusing Similar Forms

As mentioned in the Application Design section near the beginning of this article, the delivery list and delivery detail forms implement the user interface for the plan, pick up, and drop off business process steps. You implement this functionality in the same form by using a form mode parameter. This is a technique often used in Web (Web Forms) applications to make a specific Web page support multiple functions, but it can also be used with similar success in a Windows Forms–based application.

Both forms use the following enumeration (declared in the Common class).

public enum DeliveryFormMode : int
{
  Plan,
  PickUp,
  DropOff
}

When you select the Plan menu command in the main form (MainForm), the following code creates the delivery list form.

DeliveryListForm deliveryListForm =
  (DeliveryListForm)FormCache.Instance.Load(typeof(DeliveryListForm));
deliveryListForm.FormMode = DeliveryFormMode.Plan;
FormCache.Instance.Push(typeof(DeliveryListForm));

When you select the Pick Up menu command, the following code is used.

DeliveryListForm deliveryListForm =
  (DeliveryListForm)FormCache.Instance.Load(typeof(DeliveryListForm));
deliveryListForm.FormMode = DeliveryFormMode.PickUp;
FormCache.Instance.Push(typeof(DeliveryListForm));

The code for the Drop Off menu command is very similar, as shown in the following code example.

DeliveryListForm deliveryListForm =
  (DeliveryListForm)FormCache.Instance.Load(typeof(DeliveryListForm));
deliveryListForm.FormMode = DeliveryFormMode.DropOff;
FormCache.Instance.Push(typeof(DeliveryListForm));

In all three of the preceding code examples, note how the form mode is set before the form is shown (pushed).

The property in the delivery list form (DeliveryListForm) looks like the following.

public DeliveryFormMode FormMode
{
  set { this.formMode = value; }
}

The form mode then sets the heading of the form as follows.

switch(formMode)
{
  case DeliveryFormMode.Plan:
    headingLabel.Text =
      GlobalHandler.Translate.Text(
      "DeliveryListFormheadingLabelPlan", "Plan");
    break;

  case DeliveryFormMode.PickUp:
    headingLabel.Text =
      GlobalHandler.Translate.Text(
      "DeliveryListFormheadingLabelPickUp", "Pick Ups");
    break;

  case DeliveryFormMode.DropOff:
    headingLabel.Text =
      GlobalHandler.Translate.Text(
      "DeliveryListFormheadingLabelDropOff", "Drop Offs");
    break;
}

When you open the delivery details form from the delivery list form, the form mode is passed on in the same manner as for the delivery list form. In the delivery details form (DeliveryForm), the form mode modifies the user interface as follows.

switch(formMode)
{
  case DeliveryFormMode.Plan:
    if(deliveryType == (int)DeliveryFormMode.PickUp - 1)
      heading = GlobalHandler.Translate.Text(
        "DeliveryFormheadingLabelPlanPickUp", "Plan Pick Up");
    else
      heading = GlobalHandler.Translate.Text(
        "DeliveryFormheadingLabelPlanDropOff", "Plan Drop Off");
    completedCheckBox.Visible = false;
    itemsListView.CheckBoxes = false;
    break;

  case DeliveryFormMode.PickUp:
    heading = GlobalHandler.Translate.Text(
      "DeliveryFormheadingLabelPickUp", "Pick Up");
    break;

  case DeliveryFormMode.DropOff:
    heading = GlobalHandler.Translate.Text(
      "DeliveryFormheadingLabelDropOff", "Drop Off");
    contactSignatureLabel.Visible = true;
    contactSignature.Visible = true;
    minDeliveryStatus = (int)Common.DeliveryStatus.Picked;
    break;
}

The form mode determines how the heading is set and which controls appear.

Although this technique is very efficient for very similar forms, you should use it with care because too many differences between the forms will generate very complicated code that will be expensive to maintain. An alternative approach is to use inheritance with the common parts of the forms in a base class. However, when you use visual inheritance, you need to take care to make the form design efficient.

GlobalHandler

Any enterprise Pocket PC solution that will be used in more than one language needs to be easily translated. Therefore, you can use the GlobalHandler class to translate complete forms, including all controls and menus. It requires only the following single line of code in each form.

GlobalHandler.Translate.Form(this);

Even simple texts, such as error messages, can be translated by means of the following format.

MessageBox.Show(GlobalHandler.Translate.Text("MsgCantOpenWebPage",
  "Could not open web page!"), this.Text);

For more details about the management and code related to globalization, please see the article Northwind Pocket Sales: Field Sales for Windows Mobile–based Pocket PCs.

Form Cache (and Stack)

Each enterprise application that contains a large amount of forms requires that the forms, and the memory they consume, can be managed in an efficient way. Therefore, the FormCache class supports both the caching and stacking of forms. Briefly, the loading of a new form, or actually pushing a new form on the form stack, looks like the following.

FormCache.Instance.Push(typeof(SalesForm));

The push implicitly loads the form if it is not already loaded. And if any parameters need to be passed to the new form, the code looks like the following.

OptionsForm optionsForm = (OptionsForm)
    FormCache.Instance.Load(typeof(OptionsForm));
optionsForm.DatabaseExist = databaseExist;
FormCache.Instance.Push(typeof(OptionsForm));

For more details about the management and code related to caching and stacking forms, please see the article Northwind Pocket Sales: Field Sales for Windows Mobile–based Smartphones.

Help

An enterprise application should provide online Help. On Pocket PC, the standard way to support Help is through the Start menu's Help command. This command should be context sensitive to align with design guidelines. This article's sample application shows how to implement context-sensitive Help according to standards, and it requires only the following single line of code in each form.

Common.Values.HelpTopic = "PickUpForm";

For more details about the management and code related to caching and stacking forms, see the article Northwind Pocket Inventory: Logistics for Windows Mobile–based Pocket PCs.

Conclusion

Creating solutions for mobile workers, like transportation personnel, is where mobile solutions can really be valuable. By using basic network technology like sockets and by using the rapidly increasing functionality made available through XML Web services like the MapPoint Web Service, you can create very powerful and cost-effective solutions. The technology and services combined into samples like the one supplied with this article help you start and finish more rapidly.