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]
 Wednesday, October 31, 2007

I am ramping up a new project. We'll pull up a legacy eVB-project to Compact Framework. This is what I'm configuring when setting up a new dev environment:

Base Platform
Visual Studio 2005 Professional (with Service Pack 1)
Windows Mobile 6 Professional and Standard Software Development Kits Refresh
.NET Compact Framework 2.0 SP2
SQL Server Compact Edition 2005 Developer SDK
SQL Server 2005 Compact Edition Tools for Visual Studio 2005 Service Pack 1

Productivity Tools
CodeRush from Devexpress
Refactor!Pro from Devexpress
MZTools 6 from MZTools
Pocket Controller from SOTI

Controls
MobileForms Toolkit from Resco

Icon Library
IconExperience

posted on Wednesday, October 31, 2007 8:28:31 AM UTC  by Andy  #    Comments [3]
 Tuesday, October 16, 2007

So, after my post on waiting for Silverlight for Devices, I spent last night and much of today finding a way to render XAML on a web server. You can see the result in the article Silverlight on Devices, and of course the code is also included.

As expected, there are a lot of the good stuff that you miss out on using this approach (interactivity, animations, etc), but it serves the purpose of moving the design elements of your application to the new user interface platform (XAML, WPF, etc). When the technology is available on the device, the work done can be migrated to the mobile application.

In the article's sample I only render static XAML on the server, as I couldn't get the data binding to work (anyone?), but in most situations this shouldn't be a problem as you do the rendering on the server anyway.

posted on Tuesday, October 16, 2007 7:44:01 PM UTC  by Chris  #    Comments [0]

Ever since Mike Zintel's post WPF/e and the .NET Compact Framework on my 40th birthday more than two years ago, I have been waiting. I think he was right then, and I still think he is right. We need an implementation that focuses on the user interface and that allows for both online and offline. My only concern was why it was taking so long, and finally my hopes were out...

...and THEN, Robert Unoki made me green with envy when he posted on his cool work with Silverlight and Compact Framework. A bit later the video with Scott Holden appeared, and my hopes were on max again. But that was almost six months ago, and now I'm getting really inpatient. When you look at the great developer overview of the current Alpha build of Silverlight, Compact Framework isn't even on the "map" (neither is XBOX/XNA).

Even if I mostly build Enterprise applications, I want to build UXs like that (yes, Mike Zintel was right again on his theory that somewhere inside we all get into this business hoping to write video games ;-))

So, what can I do while waiting? Well, I remember that when I didn't have GDI+ on the device, I would create on-the-fly charts on a Web server that download them directly to my WinForms apps on the device. It had its drawbacks, but served my purpose. What if I could do the same with XAML on the server? I'll take a look at that soon...

posted on Tuesday, October 16, 2007 1:43:53 AM UTC  by Chris  #    Comments [0]
 Thursday, October 11, 2007

As a follow-up on my previous post about pulling data from a web site, this post is about doing it the other way -- uploading data to a web site.

Let's first fill a byte array with some content, usually from a file (or database) like this (require a reference to System.IO)...

FileStream fs = File.OpenRead("file.any");
BinaryReader br = new BinaryReader(fs);
byte[] data = br.ReadBytes((int)br.BaseStream.Length);
br.Close();
fs.Close();

...and then the code to upload looks like this (require an additional reference to System.Net)...

string url = "http://www.businessanyplace.net/file.any";
Uri uri = new Uri(url);
HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(uri);
request.Method =
"PUT";
request.Credentials =
new NetworkCredential("UserName", "P@ssW0rd");
request.PreAuthenticate =
true;
request.AllowWriteStreamBuffering =
true;
// For large files (> 50KB) you may want to uncomment the next line
//request.SendChunked = true;
request.ContentLength = data.Length;
Stream s = request.GetRequestStream();
s.Write(data, 0, data.Length);
s.Close();
HttpWebResponse response = (HttpWebResponse)request.GetResponse();
HttpStatusCode code = response.StatusCode;
string result = code.ToString();

...and this works just as well for text files as for any binary file. Please note that the user needs to have write access to the virtual directory (and the physical disk location) where the file is to be written. For a more complete example of how files (media) can be compressed (zipped) and uploaded to a Web site, see the source code for my article Claims2Go: Claims Processing for Windows Mobile-Based Devices.

posted on Thursday, October 11, 2007 9:19:43 PM UTC  by Chris  #    Comments [0]
 Wednesday, October 10, 2007

There are some questions that seem to be coming back all the time, and one of those is how to download from a web site. Whether it's a file or an image, it's basically the same approach.

I saw a nice article on DevSource name Pulling Data From Internet URLs in C# using the WebClient class which is a good alternative if you are planning on using it in the upcoming Compact Framework 3.5. But if you want a solution now, here's some code. Let's start by downloading a file (references to System.Net and System.IO)...

string url = "http://www.businessanyplace.net/images/ba.gif";
HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(new Uri(url));
request.Credentials =
new NetworkCredential("UserName", "P@ssW0rd");
request.PreAuthenticate =
true;
HttpWebResponse response = (HttpWebResponse)request.GetResponse();
Stream s = response.GetResponseStream();
int size = 2048;
byte[] data = new byte[size];
FileStream fs = File.Create("baOnDisk.gif");
while(true)
{
    size = s.Read(data, 0, data.Length);
    
if(size > 0)
        fs.Write(data, 0, size);
    
else
        break;
}
fs.Close();

...and note that the download is buffered (in 2K blocks). I've also added some credential code to make it more realistic. If you want the contents of a text file in a string, the code can be modified like this (with a reference to System.Text)...

string url = "http://www.businessanyplace.net/sample/test.htm";
HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(new Uri(url));
request.Credentials =
new NetworkCredential("UserName", "P@ssW0rd");
request.PreAuthenticate =
true;
HttpWebResponse response = (HttpWebResponse)request.GetResponse();
Stream s = response.GetResponseStream();
StringBuilder sb = new StringBuilder();
int size = 2048;
byte[] data = new byte[size];
while(true)
{
    size = s.Read(data, 0, data.Length);
    
if(size > 0)
        sb.Append(
Encoding.ASCII.GetString(data, 0, size));
    
else
        break;
}
string strData = sb.ToString();

...and to process that further (parsing the HTML, etc), you can now follow the same approach described in the article mentioned above.

posted on Wednesday, October 10, 2007 1:10:16 PM UTC  by Chris  #    Comments [0]