Monday, November 26, 2007

With VS2008 released this week, I should be diving into LINQ, WCF, and other cool stuff in the .NET CF 3.5. But it's already the end of the week, and it's time for the bigger picture. I want to say a few words about the apps that I'm missing, and all the great apps that I want to write. I use my WM device(s) every day, and there are so many things that I want to do, that I can't. Maybe I can, but it's still very difficult. Let me take a few examples. I was in my car the other day, and I wanted to look up the number to a friend. I started my browser, I searched, I got the link to the "white pages" site, I entered the name, ... I finally got the number, but it took too long, and I was already somewhat frustrated.

Person LookupI talked to a mobile veteran the other day, and I asked him what he misses the most, and his instant response was "a good mobile browser". I believe that he's right, there can definitely go more development into browser technology. But I think that the real problem is that browsing is no good on a small screen with low bandwidth. I think (know) there's another solution. As a developer, I have the liberty to write my own apps when I need some functionality on my device, and I do it with the best development tools in the world (that was just released this week). When there is a Web Service available with reasonable pricing, I just add a few controls to a form along with a Web Reference and a few lines of glue code, and I got my personal service in place (usually in a couple of minutes). There are great directory sites to find Web Services like XMmethods, RemoteMethods, APIfinder, etc, and companies like ServiceObjects, StrikeIron, CDYNE, WebservcieX, etc, are offering commercial Web Services. But even if the pricing is getting more reasonable (some services are a few cents per transaction), the number of available services are still very limited.

However, if there's no Web Service available, my only option is to get the information directly from the Web using a technique called "scraping". It's really nothing advanced, just the manipulation of the HTTP requests and responses that the browser natively handles from code. For example, let's say I want to put my earlier frustration when looking for my friend's number to an end. I would go out and look for a nice site for looking up numbers, like whitepages, and after analyzing the requests and responses (many times the "view source" in IE is sufficient, but there are also great tools like Fiddler), I write code similar to this...

// Get search result page
string
url = string.Format("http://www.whitepages.com/search/FindPerson?who={0}&where={1}",
    whoTextBox.Text, whereTextBox.Text);
HttpWebRequest request = WebRequest.Create(url)
as HttpWebRequest;
StreamReader responseReader = new StreamReader(request.GetResponse().GetResponseStream());
string responseData = responseReader.ReadToEnd();
responseReader.Close();

// More than one?
int i = 0;
if((i = responseData.IndexOf("results_multiple_widget_matching")) > -1)
{
    i = responseData.IndexOf(
"<strong>", i) + 8;
   
string matches = responseData.Substring(i, responseData.IndexOf("\n", i) - i);
    MessageBox.Show(
string.Format("{0} matches, refine search!", matches));
}
else
{
   
// Any
    if(responseData.IndexOf("class=\"fn n\">") < 0)
        MessageBox.Show(
"No matches, try again!");
   
else
    {
       
// Only one
        string s = extractValue(responseData, "fn n"); // name
        s += "\r\n" + extractValue(responseData, "street-address"); // street
        s += "\r\n" + extractValue(responseData, "locality"); // city
        s += ", " + extractValue(responseData, "region"); // state
        s += " " + extractValue(responseData, "postal-code"); // zip
        s += "\r\n" + extractValue(responseData, "tel"); // phone

        resultTextBox.Text = s;
    }
}

...and on the right you see the code in action (well, Don Box is not exactly my friend, but me and Andy sat down and talked to him after an event once, and that should count for something ;-)). The code for the private method looks like this...

private string extractValue(string s, string name)
{
   
string valueDelimiter = "class=\"" + name + "\">";

   
int valuePosition = s.IndexOf(valueDelimiter);
   
if(valuePosition < 0)
       
return string.Empty;
   
int startPosition = valuePosition + valueDelimiter.Length;
   
int endPosition = s.IndexOf("<", startPosition);

   
return s.Substring(startPosition, endPosition - startPosition);
}

...and even if helpers like this can be very useful, I clearly recommend the use of regex for more advanced scraping. Note that this approach still needs to download the whole page just to get to the data, and if the above code is running on the WM device the response time will be the same as accessing the site through the browser. That is why this code should be wrapped into a Web Service on a server somewhere, and then the WM device can access the Web Service with the few lines of code that I mentioned above for an already existing Web Service. Making the Web Service responsible for the actual scraping is also better because the clients doesn't need to be updated if there are changes to the source site.

This way, I can now easily look up both addresses and phone numbers to my friends, and it would be easy to add more functionality like calling the found phone number, etc. I should mention that whitepages has added a great mobile version of their site that you enter automatically when you go to their site with a WM device (read more about it, and see a demo), so they are no longer the best example of a service to scrape, but still, their mobile site takes much more bandwidth than the Web Service approach I described above. I don't know about you, but I prefer the clean look in the screenshot above compared to a cluttered web page.

There are so many simple services like this that I still miss, and to name a few apart from the above mentioned phone number lookup, I would like info about flights (delays), cinema search/booking, package tracking, etc, etc, etc. I guess an instant reverse phone number lookup would be a killer for anyone that wants to know who is calling right now, and the number is not in Contacts. What services are you missing?

posted on Monday, November 26, 2007 12:50:23 AM UTC  by Chris  #    Comments [0]
 Monday, November 19, 2007

Don't miss the .NET Compact Framework related "How Do I?" Videos for Devices! Each video is about 20 minutes and walks you through key development tasks. Great lab exercises and source code is downloadable.

Detecting and verifying network connection, identifying device type, monitor and managing display orientation changes, and so on. Good stuff. Find it all here…

posted on Monday, November 19, 2007 9:10:53 AM UTC  by Andy  #    Comments [0]
 Wednesday, November 14, 2007

Building further on my post about .NET, I got an e-mail from Red Five Labs about their beta release of CF for Symbian (named Net60). This company has already run a CTP of the same product without much attention from the community. Considering the operating system market shares for smartphones, this could open up a new market for .NET developers building mobile applications. It also give the traditional Symbian developer a chance to work with a great development tool (Visual Studio) and a great framework (CF).

Of course, there's much more to building applications that has the right UX than just the framework, but it opens up the possibility to write some of the code (logic, etc) that can run on most of the devices on the market. I will try to get some time to play with it, and blog more, and if you check it out, please let me know what you think...

posted on Wednesday, November 14, 2007 9:43:46 PM UTC  by Chris  #    Comments [0]
 Monday, November 12, 2007

SQL Server CE and its accompanying tools are very clear in their communication.

It's been a while since I wrote the MSDN-articles "No More Drivel: SQL Server CE Delivers" and "DataSet Server CE: Database Connectivity for Windows Mobile-Based Devices". Since then we've welcomed SQL Server 2005 Compact Edition and seen the engine moved into the ROMs of Windows Mobile devices.

I am currently upgrading an eVB-project to .NETCF, which means I am also upgrading the database from SQL Server CE 2.0.

There's a Database Upgrade Tool that upgrades databases from previous versions of SQL Server CE to the latest SQL Server 2005 Compact Edition. The tool is run on the device, is a command prompt utility, upgrade.exe, and it's located in the \Program Files\Microsoft Visual Studio 8\SmartDevices\SDK\SQL Server\Mobile\v3.0\...

The tool needs both data engines (source and destination) installed on the device in order to work. Read more here…

Since the tool is a command prompt utility, I prefer using Pocket Controller's command-line interface. The combination of command-line interfaces and path-variables can be painful on devices, so I copied the utility to the device's root, along with the original database.

I ran the upgrade-tool and it failed. The upgrade.log file said:

"Connecting to source database.
HRESULT: 0x80004005
Error Record 1
Minor: 25046"

 

I sighed. My thoughts: "Here we go again. What DLL is missing? Did I install everything correctly? Did I miss any parameters?", and believed I was up for a lengthy hunt for errors. I spent some time investigating the setup and found nothing wrong.

I picked up SQL Server CE's Books Online and looked up the error code: 25046. The documentation said: "The database file cannot be found. Check the path to the database".

I had misspelled the database name and was reminded that SQL Server CE's error codes are clear and to the point. Back on track again!

posted on Monday, November 12, 2007 11:03:19 AM UTC  by Andy  #    Comments [0]
 Thursday, November 08, 2007

For many years of building enterprise Windows Mobile applications, one of the most important design choices was the sync solution. Most of my articles include different ways of solving data sync depending on the specific application's need. Even if there are advantages with all the different technologies, like the simplicity of RDA and the flexibility/power of MR (if you're going there, you should check out the book "Windows Mobile Data Synchronization with SQL Server 2005 and SQL Server Compact 3.1" by Rob Tiffany), I have come to prefer a more service-oriented approach (most often using plain Web Services) as it gives med the control of the sync process that I want (with any logic inserted where I want). My remedy for the chunky, XML-based, communication is message-based compression. The only issue with that approach is that I need to handle much of the basic sync logic, and even if I often come to use standard code like this (on the server)...

private void update(string table, DataSet serverDataSet, DataSet clientDataSet)
{
   
foreach(DataRow dr in clientDataSet.Tables[table].Rows)
    {
        DataRow[] drs = serverDataSet.Tables[table].Select(table +
"ID='" +
         dr[table +
"ID"].ToString() + "'");
       
if(drs.Length > 0)
           
// Update
            for(int i = 0; i < dr.ItemArray.Length; i++)
                drs[0][i] = dr[i];
       
else
            // Insert
           
serverDataSet.Tables[table].Rows.Add(dr.ItemArray);
    }
   
// Delete
    foreach(DataRow dr in serverDataSet.Tables[table].Rows)
       
if(clientDataSet.Tables[table].Select(table + "ID='" +
         dr[table +
"ID"].ToString() + "'").Length < 1)
            dr.Delete();
}

...that works very well in many cases, this is hardly code that I should have to write. Wouldn't it be great with a sync framework that included all the basic sync logic, that was managed code, and that allowed me to still be in full control? Well, that is exactly what the Microsoft Sync Framework is all about. From looking at the database sync feature comparison between the different sync technologies, there is actually only one line that I don't like - the last one! After searching the web, I realized that the documentation is probably correct, there will not be support for devices (read Compact Framework) in the first release (with VS2008 at the end of November), but hopefully we don't need to wait long. The more I read the documentation, and the more I look at the code, I like what I see - this is exactly what I have been looking for! I really like the way that I can manage my own Web Service (with my own logic) and at the same time let the ServerSyncProvider take care of the data sync. As the synchronization happens, there are numerous  events that I can use to tap into the process where I want. So even if I need to be patient in waiting to use this in my mobile applications, I bet many desktop (or rather laptop) applications will gain from this framework very soon...

posted on Thursday, November 08, 2007 11:19:46 PM UTC  by Chris  #    Comments [0]
 Tuesday, November 06, 2007

There are excellent libraries available for reading (RSS) feeds on Windows Mobile, like the one from OpenNETCF (be sure to check out the sample by Alex Yakhnin). In many situations it can be a great alternative to write the complete application on the device, but one problem is that it requires considerable bandwidth (WiFi is ok, but it's not very pleasant on GPRS).

I wanted something that could allow me to just browse the titles of each feed, and if something really looks interesting, I'd spend the bandwidth to bring up the full content. As feeds are not designed for partially accessing parts of the content (when you download a feed, you get the whole file with all content), I decided to create a Web Service to pick out the content that I wanted with the following code (using the awaited System.ServiceModel.Syndication namespace)...

[WebMethod]
public DataSet GetFeedItemInfo(DataSet feedDataSet, string keyword) 
{
    // Create DataSet
    DataSet ds = new DataSet();
    DataTable dt = ds.Tables.Add();
    dt.Columns.Add("Feed"Type.GetType("System.Int32"));
    dt.Columns.Add("Title");
    dt.Columns.Add("Url");
    dt.Columns.Add("Date"Type.GetType("System.DateTime"));
    dt.Columns.Add("Keyword"Type.GetType("System.Boolean"));

    // Fill DataSet with item info for each provided feed
    for(int i = 0; i < feedDataSet.Tables[0].Rows.Count; i++)
    {
        string feedUrl = feedDataSet.Tables[0].Rows[i]["Url"].ToString();
        SyndicationFeed feed = SyndicationFeed.Load(new Uri(feedUrl));

        foreach(SyndicationItem item in feed.Items)
        {
            DataRow dr = dt.NewRow();
            dr["Feed"] = i;
            dr["Title"] = item.Title.Text;
            dr["Url"] = item.Links[0].Uri.ToString();
            dr["Date"] = item.PublishDate;
            dr["Keyword"] = item.Summary.Text.Contains(keyword);
            dt.Rows.Add(dr);
        }
    }
    ds.AcceptChanges();

    return ds;
}

...that loops through a list of feeds provided by the client. Each feed is retrieved, and only some information about each item is returned to the client. Note how the Web Service accepts a keyword, to mark those items that include the keyword. I guess it all gets clear when you see the client code...

// Specify feeds
DataSet feedDataSet = new DataSet();
DataTable dt = feedDataSet.Tables.Add();
dt.Columns.Add("Url");
DataRow dr = dt.NewRow();
dr["Url"] = "http://blog.wmdev.net/syndicationservice.asmx/getrss";
dt.Rows.Add(dr);
dr = dt.NewRow();
dr["Url"] = "http://blogs.msdn.com/netcfteam/rss.xml";
dt.Rows.Add(dr);
// ...and so on
feedDataSet.AcceptChanges();

// Get headings
FeedWebService fs = new FeedWebService();
itemInfoDataSet = fs.GetFeedItemInfo(feedDataSet, "code");

// Bind/show
listBox.DataSource = itemInfoDataSet.Tables[0];

...and the nice thing is that this works without anything else than .NET CF on the client. I can now update the list of feeds on my device, and when I sync (make the Web Service call), I get the most recent headings with a minimum of bandwidth spent. However, if I want to read the complete content of an item, I have the URL that I can use to bring it up in the device browser. A logical action would probably also be to compress the request (and response) to further minimize payload (cost), and you find code to do that in my article Two-Way .NET CF Web Service Compression. To make this really useable, the best would be to only download the content that has not already been downloaded, but hey, I need to leave something for you to work on...

posted on Tuesday, November 06, 2007 12:21:11 AM UTC  by Chris  #    Comments [1]
 Saturday, November 03, 2007

While we are (all) waiting for Silverlight for Windows Mobile, the graphics CF developer's best friend is managed GDI+, and fellow MVP Alex Feinman reminded me with his recent article Using GDI+ on Windows Mobile. Surprisingly, these excellent namespaces (System.Drawing.*) are not widely used in business applications (I know, game developers are a completely different story), although they can often drastically enhance the UX (If you haven't already, I recommend a browse through the Vista UX Guide). To get all you enterprise developers (and others) started, I want to put your attention to an article of mine, Serial Communication with the .NET Compact Framework (where you find all the code mentioned in this blog post). Even if the main focus of the article was serial communications programming, it also included some reusable managed GDI+ code.

Log ChartThe first is a log chart that can be used to display running values. In the article it's used to show the depth (see screenshot on the right), speed, and temperature. To use it, the following code can be used...

// Create chart (width, height, yMin, yMax, yStep, xMax, xStep)
depthChart =
new LogChart(pictureBox.Width, pictureBox.Height, -200, 0, 50, 180, 60);

// Add value (for reading)
depthChart.AddValue(-(
int)depthFeet);

// Draw chart
pictureBox.Image = depthChart.Paint();

...with the result for Smartphone on the right. As shown in the article, this chart has many purposes, and there are quite a few business uses too. How about live data on inventory, stocks, profit, revenue, customer satisfaction, and other time critical KPIs?

Compass ChartThe second is also a chart that may not have as many uses (but it's cooler ;-)), and this chart is used to show direction. In the article it's used to show wind direction/speed (see screenshot on the left) and heading. The following code is an example of how to use it...

// Create chart (width, height)
windChart =
new CompassChart(pictureBox.Width, pictureBox.Height)

// Draw chart (angle, topLabel, bottomLabel)
pictureBox.Image = windChart.Paint(windAngle, windSpeed, "True");

...and it couldn't be much simpler! Note that the chart has options to be shown without the boat outline and with 0-360 degrees.

A nice thing for a Windows Mobile developer about using vector graphics like this is that it adapts very well to all the different screen orientations and resolutions. As you can see in the article, the almost identical code looks very good on both Pocket PC and Smartphone.

posted on Saturday, November 03, 2007 12:09:53 AM UTC  by Your DisplayName here!  #    Comments [1]