Friday, January 11, 2008

As you know, I am busy migrating a project from eMbedded Visual Basic to .NET Compact Framework and C#. A key component in the legacy code is the way controls are populated and this is an area where I've had to workaround an issue in .NET Compact Framework.

In eVB you could do this:

Form(sControlName).Text = "Some value"

In other words, you could easily loop through all controls in a form and save names and values.

For iCounter = 0 To Form.Controls.Count - 1
sControlName = Form.Controls(iCounter).Name

You could then retrieve control names and values from the database and re-populate forms. In eVB, all controls in the form sit in the Form.Controls-collection. In .NET Compact Framework you can not address a control by its name in the Form.Controls-collection, only by its index in the collection:

Form.Controls[formControlIndex]

Furthermore, all controls in the form do not sit in the Form.Controls-collection. For instance, if you place controls in a panel, then those controls are found in the Form.Controls[IndexOfPanel],Controls-collection. I've solved these two migration issues by using a hashtable that stores the name of the control as the hash-key, and the control's index as the hash-value. In order to keep track of controls sitting in panels, I add the panel name to the control name when populating the hashtable.

First, when saving the data, I loop through the controls in the form:

foreach (Control formControl in Form.Controls)
{
   
/// Don't add data from disabled controls!
    if (formControl.Enabled)
    {
       
// Pull out control: the control, form and index
        // Is control a panel?
        if (formControl.GetType().ToString() == "System.Windows.Forms.Panel")
        {
           
// Get index of formControl, ie the panel
            formControlIndex = Form.Controls.IndexOf(formControl);

           
// Loop through controls in panel
            foreach (Control panelControl in Form.Controls[formControlIndex].Controls)
            {
                containerName = formControl.Name;

               
// Call save method. Pass ID, name of the form, the entire CONTROL that
                // sits IN the panel, and the name of the panel.
                SaveControl(ID, sFormName, panelControl, containerName);
            }
        }
       
else
        {
           
// Call save method. Pass ID, name of the form, the entire CONTROL that
            // sits IN the form, and the form name again.

            // The form name is the container.
            SaveControl(ID, sFormName, formControl, sFormName);
        }
    }
}

In the SaveControl-method, I can easily work with the control. For example, I can write:

sControlValue = ControlToSave.Text;

…to get the control's Text-property.

When opening the form I first need to create the hashtable of controls, so I can get at their index-values! The following method returns a hashtable with all controls in a form:

public Hashtable GetControls(Form form)
{
   
Hashtable formControlsHash = new Hashtable();
   
int formControlIndex;
   
int panelControlIndex;
   
string formControlName;
   
string panelControlName;

   
// Populate SmartControls with forms controls.
    foreach (Control formControl in form.Controls)
    {
       
// Get index of formControl
        formControlIndex = form.Controls.IndexOf(formControl);

       
// Get name of control.
        formControlName = formControl.Name;

       
// Add control to hashtable
        formControlsHash.Add(formControlName, formControlIndex);

       
if (formControl.GetType().ToString() == "System.Windows.Forms.Panel")
        {
           
// Loop through controls in panel
            foreach (Control panelControl in form.Controls[formControlIndex].Controls)
            {
               
// Get index of current panelControl
                panelControlIndex = form.Controls[formControlIndex].Controls.IndexOf(panelControl);

               
// Get name of panelControl. Name is constructed as "formControlName.panelControlName"
                panelControlName = formControlName + "." + panelControl.Name;

               
// Add control to listbox
                formControlsHash.Add(panelControlName, panelControlIndex);
            }
        }
    }

   
return formControlsHash;
}

The ControlName is fetched from the database and since it contains any panel-name (if the control sits in a panel), then I can get their index-values from the hashtable. Note the lines of code that are made bold:

int delimeterPosition = sControlName.IndexOf(".", 0);

// If delimeter is found, then get container name also
if (delimeterPosition > 0)
{
   
// Container is not the form
    controlName = sControlName.Substring(delimeterPosition + 1);
    containerName = sControlName.Substring(0, delimeterPosition);

   
// Get the index of the container
    indexOfContainer = (int)formControlsHash[containerName];

   
// Get the index of the control
    indexOfControl = (int
)formControlsHash[sControlName];

   
// Get the control
    controlToSet = Form.Controls[indexOfContainer].Controls[indexOfControl];

   
// Set sControlName to be the new name, after having peeled of the container name.
    sControlName = controlName;
}
else
{
   
// Get the index of the control
    indexOfControl = (int
)formControlsHash[sControlName];

   
// Get the control
    controlToSet = Form.Controls[indexOfControl];
}

Now, I have a control that I can work with and set its value!

posted on Friday, January 11, 2008 8:27:39 AM UTC  by Chris  #    Comments [0]
 Monday, January 07, 2008

I continue my series (I Want More Mobile (Web) Services and Flight Lookup Web Service) of useful Web Services for Windows Mobile with a service to check on package delivery status. If you are like me, you are inpatient when you wait for a package to arrive. I'm almost always waiting for another package to arrive, and when I have a few moments to spare, I come to think of the latest package and want to check where it was last seen. My effort is a growing creature on CodePlex called Windows Mobile Web Services where you get to the code.

packagescrape As in previous posts, you find the UX on the right, and the client code should be familiar if you've seen the previous posts...

WebServices.Service ws = new WebServices.Service();
resultTextBox.Text = ws.PackageLookup(packageTextBox.Text).Replace("\n", "\r\n");

...and on the server, the code looks like this...

[WebMethod]
public string PackageLookup(string packageNo)
{
   
// Get DHL tracking page
    string url = string.Format("http://www.dhl.com/cgi-bin/tracking.pl?AWB={0}", packageNo);
   
HttpWebRequest request = WebRequest.Create(url) as HttpWebRequest;
   
StreamReader responseReader = new StreamReader(request.GetResponse().GetResponseStream());
   
string responseData = responseReader.ReadToEnd();
    responseReader.Close();

   
Regex regex = new Regex("<a\\s*href=\\x23[0-9]*?><font(.|\\n)*?>(?<number>.*\\n?.*)</font(.|\\n)*?<a\\shref=\\\"" +
       
"(.|\\n)*?\\\">(?<origin>.*\\n?.*)</a(.|\\n)*?<a\\shref=\\\"(.|\\n)*?\\\">(?<destination>." +
       
"*\\n?.*)</a(.|\\n)*?face=\\\"arial\\\">(?<status>.*\\n?.*)<img*", RegexOptions.IgnoreCase);

   
// Extract using regex
    string s = null;
   
Match match = regex.Match(responseData);
   
if(match.Success)
    {
       
//match.Groups["number"];
        s = string.Format("Origin: {0}\nDest.: {1}\nStatus: {2}",
            match.Groups[
"origin"], match.Groups["destination"],
            match.Groups[
"status"].ToString().Replace("<BR>", "\n"));
    }
   
else
        throw new Exception("Not found, try again!");
   
   
return s;
}

...and as you can see, I'm looking up DHL shipments. It shouldn't be too hard to extend it to cover other couriers as well.

This time I have used a regular expression (regex) to extract the key information from the web page. As you can see in the code above, the code is simpler - if you understand the regex. The regex extracts four pieces of info: the shipping number (which is not used), the package pick up location (origin), the package drop off location (destination), and the current status of the shipment. Regular expression is a very powerful concept that can be used for many things, and it's especially helpful when scraping web pages. My favorite tools for working with regex are Expresso and Regulator, and Regulator is especially useful for .NET development as it's written using .NET (yes, unfortunately there are some minor differences between different implementations).

posted on Monday, January 07, 2008 6:18:42 AM UTC  by Your DisplayName here!  #    Comments [0]