It's very typical for developers to focus on enterprise applications (with
databases, application servers, etc.) without paying much attention to PDAs.
In the past, PDA developers were a separate community. And there is a reason
for that they usually face a lot of unique challenges: slower processors,
limited memory, small storage space, and use of a specific language.
Because of this, PDA development is often perceived by the casual
programmer as some sort of "unneeded art" we all would rather write code
for a good old 8-processor/4GB RAM Unix box (or something like that) and
simply use a recently purchased PDA as a phone book.
Microsoft .NET decided to change all this. Development of .NET Compact
Framework applications is really not that different from development of
regular applications. Besides, it's possible to write a program that will
work on a regular PC as well as on a Compaq iPAQ, for instance.
How It All Started
Let me describe my first impressions of .NET. .NET supports a variety of
application types I will focus on the two most common ones, Windows Forms
and ASP.NET/Web services applications.
As the name suggests, Windows Forms is a regular Windows (or PDA there
is really very little difference in development) application. It has two
major advantages over Java: the Designer, which allows you to draw your
screens; and the amount of available controls an absolutely amazing
selection (keep in mind, though, that the Compact Framework is somewhat more limited). Microsoft also did a great job connecting the Design view with the code. Whenever you place a control, the IDE just adds the appropriate code to your source no more proprietary binary files! Thus, developers can always choose between using the Designer and writing the entire application's source using Notepad. The source of the application
looks similar to an old Visual Basic app you need to set some properties
(always programmatically, even when set in the IDE), call some methods, and
handle events. No Windows Message loops or strange preprocessor directives
(MFC guys surely will miss them). What's more, if you want to create your
own control you don't have to deal with COM or ActiveX your control is
just a regular class!
However, the biggest surprise was ASP.NET. What would you say about
using an IDE for development of Web-based applications? (I am probably one
of the rare Java developers who still prefers an IDE over Notepad and vi
editors) Microsoft chose the same approach as JSP the final document is
compiled on the server. The developer puts the controls on the ASP.NET page,
sets properties, calls methods, and handles the events in pretty much the
same way as in Windows Forms. The IDE generates a page (.aspx) and a class
file. The class file resembles JSP's JavaBean; however, there is a
difference the ASP.NET class file is entirely stateless and cannot be
bound to a session or an application. Instead, you have access to session,
application, and so on, from that class, but you have to do the work
yourself. There is support for custom controls you could easily write one
yourself.
A separate subject is Web services. Creation of a Web service is
absolutely trivial you just add the method to the class generated by
Visual Studio Framework. The use of the Web service is even easier just
provide a URL to a wizard, and it will create all the wrapper classes. You
also have absolute control; you can modify the wrappers in any way you want,
create your own from scratch, or just use the generic APIs.
And finally, the new C# language you don't have to learn it. You can
write code in VB, C++, C#, and JavaScript, and it will all result in the
same bytecode that will be used by the CLR (Common Language Runtime). If you
know C++/Java, you'll be able to write in C# in a matter of a few hours. To
my mind the language is significantly more advanced than Java, plus there's
a lot of support (tool tips, pop-ups, etc.) from the IDE.
Writing a Compact Framework Application
Let's concentrate on development of a Compact Framework application. I'm
not going to describe in detail how to write your first Hello World PDA
program it's almost as simple as putting an elephant into the refrigerator
(open the refrigerator, put an elephant in, close the refrigerator).
Instead, we'll make something more complex, an application that uses a Web
service, and later convert it to a Custom Control.
In order to build a Compact Framework application, you need to choose
the Smart Device Application option. The Application Wizard allows you to choose between a Pocket PC (iPAQ, for instance) and a Windows CE application. We'll develop a Windows
application. The IDE generates a form and displays it in the Design view.
Remember, Pocket PCs do not have big screens, and this is all the real
estate we have. Also note that the IDE automatically adds the Menu
(mainMenu1). Even if you're not planning to have a menu, don't delete it
because it's needed to display an input keyboard on the screen.
In this example, we'll use one of the Web services available at
Microsoft the MapPoint .NET Web service. The service provides geographical
maps, driving directions, points of interest, and so on. The Help files
provided with Developer Studio contain all the information about the
service.
Let's discuss the following application: the user enters the latitude
and longitude of the center and the dimensions of the area in miles and
presses the Display button. The application displays a map of the selected
area (actually, we'll use a lot of code provided in the article "Map
Rendering Basics in MapPoint .NET," at
http://msdn.microsoft.com/vbasic/
techinfo/articles/default.asp.
The experience of writing a Windows Forms application for a PDA is no
different from writing it for a PC. However, for people who have never
written a .NET application, I describe some of the major steps.
Our application will probably have two screens the one where a user
enters the request and the one with the image and the message. This means
we'll have to show some of the controls and hide some of them when the
buttons are pressed. However, they all have to live within one form. How do
we put them all in the Designer without making a real mess? I suggest
creating another test form and using it as a workspace (a new form can be
added in Solution Explorer by clicking on the project not on the Solution
and choosing Add>Add New Item>Windows Form).
Our application will use Panel Control (all controls can be found in the
Toolbox on the left side under Device Controls). The panels are very useful,
serving as a container for other controls, and you can show/hide a lot of
controls at once. For example, requestPanel will contain input controls (see
Figure 1).
Now we need to move all this to the real form. It's actually pretty easy
the IDE allows full copy/paste. Even the names will be preserved, given
that they were changed from Microsoft's favorites (button1, panel1, etc.).
The only thing left to do is create an event handler (a button with a
lightning bolt sign in the Properties window opens the events-handling
window for the control where you can specify the event handlers, see Figure
2).
Similarly, the second panel, mapPanel, will contain PictureBox
(mapPicture) and a "Back" button (btnBack). Then the event for the "Back"
button can be created in exactly the same way that the panel can be copied
to the main application. The only difference is that this panel should not
be initially visible; the "Visible" property of the panel will have to be
set to false.
Behind the scenes, the IDE translates all our actions in the Designer to
the code inside the InitializeComponent method (whenever you do not see this
method, click on the little "+" next to "Windows Form Designer generated
code"). There are also "Main" function and two event handlers.
Adding Web services to the application is very trivial and completely
the same for a PDA and a PC. The easiest way is to right-click on References
in Solution Explorer, select "Add Web Reference", and follow the Web
References Wizard. The only recommendation is to avoid using the names of
the directories as created by the Wizard I suggest renaming them to
something more meaningful. For instance, I use MapPoint directory for the
MapPoint .NET Web service (Figure 3).
Now we're ready to implement the Display button Click event handler. All
it needs to do is hide the request panel, show the map panel, and call the
DisplayMap function that will do all the work (see Listing 1).
The handler for the "Back" button is also quite simple all we need to
do is to hide the map panel and show the request panel.
Last, we need to set both the "MaximizeBox" and "MinimizeBox" properties
of the Form to False. This way, instead of "minimize" (the button with the
"X") you will see a "close" button with tiny "OK" inside (see Figure 4).
We can test the application on the real device and on the emulator. The
emulator really demonstrates the behavior of the application on the device;
just remember that the real device may not have a mouse! I have noticed
slight issues with the emulator. Here are my recommendations:
- Start the emulator
- Perform hard reset
- Save the state of the emulator
This way the emulator will always start in a "clean" state. Do not close
the emulator when you want to go back to development just stop the
application. However, if you must restart the emulator, do not save the
state again; otherwise, the clean state will be overwritten.
The code we've just discussed uses a synchronous call to Web services.
Users will certainly see a delay between pressing the "Display" button and
the display of the picture.
To add some animation or text we would need to use an asynchronous call.
This isn't very hard we just need to replace the call that retrieves data
from the service (GetMap) with the following:
IAsyncResult ares= mpRenderService.BeginGetMap
(myMapViews, myMapOptions, myPushPins);
while(!ares.IsCompleted)
{
Thread.Sleep(1000);
this.waitMessage.Text=.....
this.Update();
}
myMaps = mpRenderService.EndGetMap(ares);
The BeginXXX method of the Web service initiates the asynchronous
operation. Monitoring of the IsCompleted property of returned IAsyncResult
allows finding when the operation is completed. It is generally better to
put some delay in the waiting loop that's why Thread.Sleep is used. The
Update call is used to refresh the screen otherwise the new text of the
waitMessage will not be displayed.
From Application to Custom Control
Custom-control code is surprisingly similar to the Form that was used in
the application.
Writing a custom control requires creation of a new project Class
Library will have to be chosen this time. Unfortunately, at the moment
Microsoft does not provide a Designer for Custom Controls. Here are my
recommendations:
Create a form in a Windows Forms project
Put in all the controls, code, etc.
Add the form to the Class Library project. The form can be added in
Solution Explorer by clicking on the project not on the Solution and
choosing Add/Add Existing Item. However, Class Library project does not
include as default the reference to System.Windows.Forms it will also have to be added (right-click on References and choose Add to see a References dialog).
Make the form into a control, which is pretty simple: modify the code
to reflect the fact that the form is no longer a separate application,
create properties, create methods, and change the class inheritance it
should inherit from System.Windows.Forms.Control. If the control uses Web services they would have to be added to the project.
Let's examine the changes that would have to be made with Form1.cs to
make a control that displays only the map for given center point and area
dimensions. First we need to update some controls (for instance change the
visibility of mapPanel) and remove from Form1.cs all the controls that are
no longer needed (input panel, buttons, most of the labels, menu). We also
do not need the Main function any more. Change the inheritance to
System.Windows.Forms.Control; remove the event handlers for the "Back" button and the "Display" button. Change the visibility of the void DisplayMap() method to public.
This way the control's container will be able to call it directly. Now we
add properties to set the request information needed by Web service calls.
At this stage the control will have all its functionality, but it is not
ready to be used in the IDE we need to mask or add the code that will be
used in Design mode only.
The best way to do this is to use a special preprocessor variable,
NETCFDESIGNTIME, and later compile the control twice once for Design mode
and once for runtime. For example, we might want to add another PictureBox,
logoImage, to be displayed only in design mode. In this case the
InitializeComponent method would look like Listing 2.
The call uses the resource from the assembly logo.jpg image must be
added to the project and changed; its Build Action property should be
modified to "Embedded Resource".
Now that we have all the code for the control, we need to build it.
Unfortunately, if you use the Build command from the IDE you will end up
with only the runtime version of the control Microsoft did not provide us
with an easy way to build both. The following command can be executed in
order to build the debug version (I actually recommend making it a batch
file) (see Listing 3).
Please note that all System.CF.*.dll are actually installed under ...\Microsoft Visual Studio .NET2003\CompactFrameworkSDK\<version>\Windows CE\Designer so the path will have to be specified. Now both controls are to be copied to the appropriate directories:
<control>.dll should go to ...\Microsoft Visual Studio .NET
2003\CompactFrameworkSDK\<version>\Windows CE\ and <control>.Design.dll to
...\Microsoft Visual Studio .NET 2003\CompactFrameworkSDK\<version>\Windows CE\Designer.
A Word About Enterprise Architecture
The ability to use Web services in Compact Framework applications opens
new horizons for the use of PDAs in enterprise systems. When all the
business logic runs on the enterprise server, there's a lot of freedom for
implementation you could implement all the logic inside Web services or
have Web services serve as gateways to other sources, for instance
Enterprise JavaBeans.
Summary
The Microsoft .NET Compact Framework also brings new opportunities for
enterprise programmers now they can not only integrate PDA applications
with their systems, but also write all the code themselves. With .NET,
writing PDA applications is very similar to writing regular Windows
applications and generally doesn't require any special skills.
Author Bio
Roman Smolgovsky is responsible for architecting industry-leading flight
intelligence solutions as well as managing the products development process at FlyteComm, Inc. He brings over 15 years of experience in system architecture, design, implementation, and deployment of software solutions and has a history of leading
development teams to success. Roman holds on MS in computer science from the
University of Radio Engineering Minsk, Minsk USSR.
romans@flytecomm.com
Listing 1
private void DisplayMap()
{
// Set the proper distance units - the default is kilometers
MapPoint.DistanceUnit myDistanceUnit =
MapPoint.DistanceUnit.Mile;
MapPoint.UserInfoRenderHeader myUserInfoHeader =
new MapPoint.UserInfoRenderHeader();
myUserInfoHeader.DefaultDistanceUnit = myDistanceUnit;
// Initialize the Render Web Service
MapPoint.RenderServiceSoap mpRenderService=new
MapPoint.RenderServiceSoap();
mpRenderService.Credentials= <... your credentials ...>
mpRenderService.PreAuthenticate=true;
mpRenderService.UserInfoRenderHeaderValue=myUserInfoHeader;
MapPoint.MapView [] myMapViews = new
MapPoint.MapView[1];
MapPoint.DataSource myDataSource = new
MapPoint.DataSource();
MapPoint.MapOptions myMapOptions = new
MapPoint.MapOptions();
MapPoint.LatLongCoord myLatLong = new
MapPoint.LatLongCoord();
MapPoint.ImageFormat myImageFormat = new
MapPoint.ImageFormat();
MapPoint.Pushpin [] myPushPins = new
MapPoint.Pushpin[1];
MapPoint.MapImage [] myMaps;
//Set the world map as the DataSource.
myDataSource.Name = "MapPoint.World";
myMapOptions.GeoDataSource = myDataSource;
//Set image size to match the dimensions
myImageFormat.Width = this.mapPicture.Width;
myImageFormat.Height = this.mapPicture.Height;
myMapOptions.Format = myImageFormat;
//Create and define MapView.
myMapViews[0] = new MapPoint.MapView();
//Set the center point
myLatLong.Latitude = Convert.ToDouble(this.textBoxLat.Text);
myLatLong.Longitude = Convert.ToDouble(this.textBoxLon.Text);
myMapViews[0].CenterPoint = myLatLong;
//Set the area size
myMapViews[0].Height = Convert.ToDouble(this.textBoxHeight.Text);
myMapViews[0].Width = Convert.ToDouble(this.textBoxWidth.Text);
`
//Place a PushPin at the CenterPoint.
myPushPins[0] = new MapPoint.Pushpin();
myPushPins[0].Label =
string.Format("({0},{1})",this.textBoxLat.Text,this.textBoxLon.Text);
myPushPins[0].LatLong = myLatLong;
myPushPins[0].IconDataSource = "MapPoint.Icons";
myPushPins[0].IconName = "5";
myPushPins[0].PinID = "Pin1";
//Request the map.
myMaps = mpRenderService.GetMap(myMapViews,
myMapOptions, myPushPins);
this.mapPicture.Image =
new Bitmap(new System.IO.MemoryStream(myMaps[0].Image.Bits));
}
Listing 2
private void InitializeComponent()
{
this.mapPanel = new System.Windows.Forms.Panel();
this.mapPicture = new
System.Windows.Forms.PictureBox();
//
// mapPanel
//
this.mapPanel.Controls.Add(this.mapPicture);
this.mapPanel.Location = new System.Drawing.Point(0, 0);
this.mapPanel.Size = new System.Drawing.Size(224, 248);
this.mapPanel.Visible = true;
//
// mapPicture
//
this.mapPicture.Size = new System.Drawing.Size(224, 208);
//
// Control
//
#if NETCFDESIGNTIME
// logo image
this.logoImage = new System.Windows.Forms.PictureBox();
this.logoImage.Location = new System.Draw-
ing.Point(0, 0);
this.logoImage.Size = this.Size;
this.logoImage.SizeMode=PictureBoxSizeMode.CenterImage;
this.logoImage.Image= new Bitmap(
System.Reflection.Assembly.
GetExecutingAssembly().
GetManifestResourceStream("logo.jpg") );
this.Controls.Add(this.logoImage);
#endif
this.Controls.Add(this.mapPanel);
}
Listing 3
csc /noconfig /define:NETCFDESIGNTIME /target:library /out:<name of your
control>.Design.dll /recurse:*.cs
/res:<name of the embedded resource i.e. logo.jpg>
/r:<path>/System.CF.Design.dll
/r:<path>/System.CF.Windows.Forms.dll
/r:<path>/System.CF.Drawing.dll
/r:System.Windows.Forms.dll
/r:System.Drawing.dll
/r:System.dll
/r:System.XML.dll
/r:System.Web.Services.dll
/r:System.Data.dll
/nowarn:1595
All Rights Reserved
Copyright © 2004 SYS-CON Media, Inc.
E-mail:
info@sys-con.com