Click here to Skip to main content
15,867,765 members
Articles / Programming Languages / C#

Consuming A Weather Web Service With MyXaml

Rate me:
Please Sign up or sign in to vote.
4.77/5 (25 votes)
8 May 20049 min read 144.9K   2   43   14
A demonstration of using MyXaml to consume a web service.

Introduction

I wanted to play with web services with regards to how they might be implemented in MyXaml, and I figured an interesting web service to try out is one that reports the current weather conditions and a forecast.

It was interesting trying to even find a working weather web service. Most of the ones I tried were dead or didn't work. I finally stumbled upon the Code Project article "Writing a Web Service Consumer using Borland C# Builder" by Sagar Regmi, and after investigating the EJSE site, I discovered that it has everything that I need:

  • current conditions
  • forecast
  • nice bitmaps

The latter, nice bitmaps, I stumbled across a weblog that pointed out that the icons are available from EJSE also (I think this is the weblog where I found the information).

For our international community, sorry, but this web service only works in the US.

How to Create the Web Service Consumer

Creating the web service consumer is trivial:

  1. Create a new project as a Class Library
  2. Right click on "References" and select "Add Web Reference"
  3. Copy the link to the web service's WSDL into the URL textbox
  4. Click on "Go"
  5. Click on "Add Reference"

Visual Studio creates a nice set of wrapper classes for invoking the web service, classes that encapsulate the return data, and enumerations.

Creating the MyXaml Application

The MyXaml application consists of several parts:

  1. The namespaces that reference the assemblies to be used in the application
  2. The form declaration
  3. The service invocations
  4. The current conditions controls
  5. The forecast controls

The following sections describe each of these.

Creating the Namespaces in MyXaml

There are four XML namespaces that need to be created:

  1. The default namespace, referencing System.Windows.Forms
  2. The namespace for the XWorkflow plug-in
  3. The namespace for the newly created web service consumer
  4. The namespace for the object definitions

The result looks like this:

XML
<?xml version="1.0" encoding="utf-8"?>
<!-- (c) 2004 Marc Clifton All Rights Reserved -->
<MyXaml
    xmlns="System.Windows.Forms"
    xmlns:def="Definition"
    xmlns:wf="XWorkflow"
    xmlns:ws="WeatherForecast.com.ejse.www, WeatherForecast">

The namespace declarations take the form:

  • Namespace

    -or-

  • Namespace, Assembly

    -or-

  • Namespace, Fully Qualified Assembly Name

When the namespace and the assembly are the same, the first form is sufficient. When the namespace is different from the assembly name, then the second form must be used. The third form is useful if you want to reference a specific assembly version or culture.

The Form Declaration

The form declaration is straight-forward:

XML
<Form def:Name='AppMainForm'
           ClientSize='525, 460'
           StartPosition='CenterScreen'
           FormBorderStyle="FixedSingle"
           Text='Weather Forecast'>

These are all properties of the .NET Form class. The "def:Name" attribute instructs the parser to keep track of the instance in a key/value pair list. The key is always the attribute value, and the value is the class instance.

The Service Invocations

First, the service must be instantiated. MyXaml isn't just a UI markup parser. It can instantiate any parameter-less class. In this case:

XML
<ws:Service def:Name="Service"/>

MyXaml is instantiating the service. The "ws:" prefix tells the parser that the class is found in the assembly referenced by the previous namespace declaration. Again, the "def:" prefix is used, which tells the parser to save a reference to the instance just created. Even though the Service class doesn't have a Name property, the above statement is still processed.

Next, two methods of the service are called:

XML
<wf:Invoke DefList="{MyXamlDefs}" Target="{Service}"
                  Method="GetWeatherInfo" Args="[int]02852" RetVal="Info"/>
<wf:Invoke DefList="{MyXamlDefs}" Target="{Service}"
                  Method="GetExtendedWeatherInfo" Args="[int]02852" RetVal="ExtInfo"/>

To get your local weather, edit the file and replace "02852" with your zipcode.

While MyXaml supports inline and code-behind, sometimes that's overkill. All we really want to do is call a method, passing some parameters to it, and possibly do something with the return value.

The above two lines instantiate the class Invoke, found in the XWorkflow assembly (as determined by the "wf:" prefix). For every class that is instantiated, the parser checks, via reflection, if it has a "Finisher" method. if so, this method is invoked. In the case of the Invoke class, the Finisher method is defined to invoke, via reflection, the method initialized in the XML attributes:

  • DefList - This is the definition list to use to resolve references. In this case, we're passing MyXaml's own definition list, which it adds to its own key/value object list so that it can be referenced in the markup. The {} braces tell the parser to look up the object in the object list.
  • Target - This is the object on which we are calling a method. The {} braces tell the parser to look up the object in the object list, which is why we used the "def:" format in naming the Service instance.
  • Method - This is the method to invoke.
  • Args - These are the arguments, comma delimited. Note the kludge to convert the string to an "int".
  • RetVal - If defined, the return value (an object, we don't care about type) is added to the definition list. The attribute value is the key in the key/value pair.

In this particular case, the WeatherInfo and ExtendedWeatherInfo return instances are being stored in the MyXaml definition list, using the name keys "Info" and "ExtInfo".

What does the Invoke code look like? There are a couple interesting parts:

Processing the Args Attribute

C#
public Type[] ProcessParams(object[] parms)
{
    ArrayList types=new ArrayList();
    for (int i=0; i<parms.Length; i++)
    {
        string parm=parms[i].ToString();
        if (parm.StartsWith("*"))
        {
            parm=Lib.StringHelpers.RightOf(parm, '*');
            if (defList.Contains(parm))
            {
                object obj=defList[parm];
                parms[i]=obj;
            }
        }
        else if (parm.StartsWith("["))
        {
            string parmType=Lib.StringHelpers.Between(parm, '[', ']');
            if (parmType=="int")
            {
                parms[i]=Convert.ToInt32(
                   Lib.StringHelpers.RightOf(parm, ']'));
            }
        }
        types.Add(parms[i].GetType());
    }
    return (Type[])types.ToArray(typeof(Type));
}

Yes, there's a serious kludge in this code, regarding testing for the "[int]" substring. I'll be fixing that in the next version of MyXaml. But the interesting thing about this code is how it builds the type array, which is needed to find the specific method that accepts the particular parameter types (often, .NET can resolve this on its own. Under certain cases though, it can't, which results in an exception being thrown, that ambiguous parameters are resulting in two or more matching methods, hence I go through the pains of getting the type information myself).

Invoking the Method via Reflection

The following code calls the method and handles the return value:

C#
public void Finisher()
{
    Type t=target.GetType();
    object[] parms=ArgList.ToArray();
    Type[] types=ProcessParams(parms);
    MethodInfo mi=t.GetMethod(method, types);
    object ret=null;
    if (mi != null)
    {
        try
        {
            ret=mi.Invoke(target, parms);
            if (RetVal != null)
            {
                defList[RetVal]=ret;
            }
        }
        catch(Exception e)
        {
            ...
        }
    }
    else
    {
        ...
    }
}

The Current Conditions Controls

The markup for the current conditions section of the UI is simple enough. It's a bunch of labels:

XML
<Controls>
    <GroupBox Location="10, 10" Size="500, 230" Text="Current Conditions"
                   FlatStyle="System" Font="MS Sans Serif, 10pt">
        <Controls>
            <Label Location="10, 20" Size="100, 20" Text="Location:"/>
            <Label Location="10, 40" Size="100, 20" Text="Last Updated:"/>
            <Label Location="10, 80" Size="100, 20" Text="Temperature:"/>
            <Label Location="10, 100" Size="100, 20" Text="Feel like:"/>
            <Label Location="10, 120" Size="100, 20" Text="Humidity:"/>
            <Label Location="10, 140" Size="100, 20" Text="Pressure:"/>
            <Label Location="10, 160" Size="100, 20" Text="UV Index:"/>
            <Label Location="10, 180" Size="100, 20" Text="Wind:"/>
            <Label Location="10, 200" Size="100, 20" Text="Forecast:"/>

            <Label Location="110, 20" Size="200, 20" Text="{Info.Location}"/>
            <Label Location="110, 40" Size="380, 40" Text="{Info.LastUpdated}"/>
            <Label Location="110, 80" Size="200, 20" Text="{Info.Temprature}"/>
            <Label Location="110, 100" Size="200, 20" Text="{Info.FeelsLike}"/>
            <Label Location="110, 120" Size="200, 20" Text="{Info.Humidity}"/>
            <Label Location="110, 140" Size="200, 20" Text="{Info.Pressure}"/>
            <Label Location="110, 160" Size="200, 20" Text="{Info.UVIndex}"/>
            <Label Location="110, 180" Size="200, 20" Text="{Info.Wind}"/>
            <Label Location="110, 200" Size="200, 20" Text="{Info.Forecast}"/>
        </Controls>
    </GroupBox>

The interesting thing here is the Text="{Info.Location}" and similar attributes. This syntax instructs the parser to return the object referenced inside the {} braces. However, instead of just returning the object, the dot notation is used to tell the parser to return the value of the object's property, as named on the right side of the ".". This syntax can be used to drill down into properties to any depth, and is a very convenient way of getting access to object property values.

The Forecast Controls

The five day forecast employs a really neat feature of MyXaml, the ability to include repetitive blocks of XML. This feature is used to display the Day1, Day2, Day3, Day4, and Day5 members of the ExtendedWeatherInfo instance, all of which are DayForecastInfo objects. Because the web service is nicely structured in this way, we can employ the prefix/postfix feature of the Include processor to adjust for the specific member instance that we're interested in.

Remember that the parser instantiates any kind of class. Again, the Include tag is implemented with a class named Include. The core parser doesn't know and doesn't care about includes, or anything else specific about the class that it is instantiating. This is a very important thing. Any "custom" parsing is handled by classes that are instantiated by the core parser. Essentially, MyXaml is a plug-in framework.

Here's the rest of the markup:

XML
            <GroupBox Location="10, 250" Size="100, 190" 
             Text="{ExtInfo.Day1.Day}" FlatStyle="System">
                <Controls>
                    <Include Src="ExtForecast.mx" 
                    ElementName="ExtendedForecast" Postfix="1"/>
                </Controls>
            </GroupBox>
            <GroupBox Location="110, 250" Size="100, 190" 
            Text="{ExtInfo.Day2.Day}" FlatStyle="System">
                <Controls>
                    <Include Src="ExtForecast.mx" ElementName="
                    ExtendedForecast" Postfix="2"/>
                </Controls>
            </GroupBox>
            <GroupBox Location="210, 250" Size="100, 190" 
            Text="{ExtInfo.Day3.Day}" FlatStyle="System">
                <Controls>
                    <Include Src="ExtForecast.mx" 
                    ElementName="ExtendedForecast" Postfix="3"/>
                </Controls>
            </GroupBox>
            <GroupBox Location="310, 250" Size="100, 190" 
            Text="{ExtInfo.Day4.Day}" FlatStyle="System">
                <Controls>
                    <Include Src="ExtForecast.mx" 
                    ElementName="ExtendedForecast" Postfix="4"/>
                </Controls>
            </GroupBox>
            <GroupBox Location="410, 250" Size="100, 190" 
            Text="{ExtInfo.Day5.Day}" FlatStyle="System">
                <Controls>
                    <Include Src="ExtForecast.mx" 
                    ElementName="ExtendedForecast" Postfix="5"/>
                </Controls>
            </GroupBox>
        </Controls>
    </Form>
</MyXaml>

For each GroupBox, note how the text is being set to "{ExtInfo.Day<n>.Day}", where <n> is for days 1-5. The second thing to note is the "Postfix" property, which specifies a value. The Include class allows you to define a prefix and a postfix value, which can be applied anywhere in the included markup, using the #prefix# and #postfix# notation.

OK, the notation is a bit nutso, isn't it? Well, regardless, I've used this feature on numerous occasions in the years of application development that I've done with the Application Automation Layer, and I can attest to its incredible flexibility and power.

The Forecast Include File

So, what does the include file look like? Here's the markup:

XML
<?xml version="1.0" encoding="utf-8"?>
<!-- (c) 2004 Marc Clifton All Rights Reserved -->
<MyXaml
    xmlns="System.Windows.Forms"
    xmlns:def="Definition"
    xmlns:wf="XWorkflow"
    xmlns:ws="WeatherForecast.com.ejse.www, WeatherForecast">
    <Element Name="ExtendedForecast">
        <PictureBox Location="22, 20" Size="56, 48">
            <Image>
                <Bitmap URL="http://www.ejse.com/WeatherService/images/52/
                                   ExtInfo.Day#postfix#.IconIndex}.gif"/>
            </Image>
        </PictureBox>
        <Label Location="10, 80" Size="80, 25"
                    Text="{ExtInfo.Day#postfix#.Forecast}" TextAlign="MiddleCenter"/>
        <Label Location="10, 105" Size="80, 20"
                    Text="Precip. Prob.:" TextAlign="MiddleCenter"/>
        <Label Location="10, 125" Size="80, 20"
                    Text="{ExtInfo.Day#postfix#.PrecipChance}" TextAlign="MiddleCenter"/>
        <Label Location="10, 145" Size="80, 20"
                    Text="{ExtInfo.Day#postfix#.High}" TextAlign="MiddleCenter" ForeColor="Red"
                    Font="MS Sans Serif, 10pt, style=Bold"/>
        <Label Location="10, 165" Size="80, 20"
                    Text="{ExtInfo.Day#postfix#.Low}" TextAlign="MiddleCenter" ForeColor="Blue"
                    Font="MS Sans Serif, 10pt, style=Bold"/>
    </Element>
</MyXaml>

Here, we see a few things. First off, is the PictureBox. MyXaml complies with the concept of tags being in a "class-property-class" hierarchy (although in cases where the property name and the property value, as implemented by a class, are the same, you can use the "class-class" format):

  1. PictureBox is a class.
  2. Image is a property of that class, which just so happens to be implemented by an Image class, but we can't use it directly because of the way it is implemented.
  3. The Bitmap class in MyXaml returns an Image, and has some smarts in it--the image can be obtained from a URL, file, or resource.

The parser, following the "class-property-class" rule, assigns the object constructed by the Bitmap class to the Image property of the PictureBox instance.

Note the URL. Using the #postfix# syntax, we can extract the IconIndex referenced by the particular day. Similarly, the forecast, precipitation probability, high and low temperature text is extracted for the instance determined by the #postfix# contents.

When markup is included, a pre-processor implemented in the Include class goes through the attribute values and replaces all occurrences of #prefix# and #postfix# with the values specified in the Include tag. Yes, this is time consuming and could stand for a lot of optimization. Regardless, the functionality that this feature provides is quite powerful. It keeps us from having to write the same markup over and over (in this case, 5 times over)!

Conclusion

Besides demonstrating a simple web service, I hope that primarily this article gives you some interesting ideas regarding how XML can be used to declaratively instantiate classes. Some of the techniques, such as the Include feature, are really nifty--you can create a library of UI building blocks, for example. Remember, this isn't limited to UI though. In my blog, I demonstrate using the Include tag to load in a DataTable of states and their abbreviations.

Using MyXaml, or any general purpose declarative instantiator engine, really changes the way in which you think about programming. Besides helping to decouple objects, this method of programming really helps to separate the UI from the control logic. If you generalize that idea, you can see that it creates a nice separation between the "passive" parts of the program and the "active" parts. I've found that, a lot of times, the passive parts change over time while the active parts remain the same. Being able to easily modify the UI, data tables, or other declaratively initialized information ends up making my applications more robust. I'm not recompiling code, the application is more resilient to new and existing feature changes, and I can get my work done faster (yes, even without a designer!).

License

This article has no explicit license attached to it, but may contain usage terms in the article text or the download files themselves. If in doubt, please contact the author via the discussion board below. A list of licenses authors might use can be found here.


Written By
Architect Interacx
United States United States
Blog: https://marcclifton.wordpress.com/
Home Page: http://www.marcclifton.com
Research: http://www.higherorderprogramming.com/
GitHub: https://github.com/cliftonm

All my life I have been passionate about architecture / software design, as this is the cornerstone to a maintainable and extensible application. As such, I have enjoyed exploring some crazy ideas and discovering that they are not so crazy after all. I also love writing about my ideas and seeing the community response. As a consultant, I've enjoyed working in a wide range of industries such as aerospace, boatyard management, remote sensing, emergency services / data management, and casino operations. I've done a variety of pro-bono work non-profit organizations related to nature conservancy, drug recovery and women's health.

Comments and Discussions

 
Generalcannot get the weather Pin
erodevadivel9-Aug-07 4:38
erodevadivel9-Aug-07 4:38 
GeneralRe: cannot get the weather Pin
Marc Clifton9-Aug-07 6:16
mvaMarc Clifton9-Aug-07 6:16 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.