Tag Archives: Rasters

GeoRSS feed with map tips in the Web ADF

In this post, guest author Sam Berg of ESRI Boston explains how to link map tips to a GeoRSS feed using the Web ADF:

The ArcGIS Server 9.2 Web ADF provides a very useful capability in its support of web-tier graphics resources. Because the underlying map services in an application are not therefore required to draw all content, the web application instead can not only add dynamic data directly, but also more efficiently manage this custom content of the web map. The ArcGIS Server framework will take care of rendering all the data- we just need to customize the application to load into the web-tier graphics resources the content that our users will be interested in seeing.

When you combine adding data to your map at run time along with utilizing the out of the box MapTips web control, there is a real opportunity to build some easy-to-use web maps, complete with pop-up map tips for the added content.

Perhaps one of the most natural use cases for this kind of approach would be to plot GeoRSS feeds from a published resource on the web. GeoRSS feeds contain what you might expect: XML content encoded with associated geographic coordinates. There are a variety of sources for this content, and one great example is one from the USGS Earthquake Center that contains the location and magnitude information for worldwide earthquake activity over the past 24 hours (http://earthquake.usgs.gov/eqcenter/recenteqsww/catalogs/eqs1day-M2.5.xml).

There are some good GeoRSS feeds out there, but what about all the RSS news feeds that you are currently viewing with your favorite online RSS aggregator? Actually, there are a few web services out there that will read a designated RSS feed and return for you a GeoRSS version with <geo:lat> and <geo:lon> tags. Nice!

The web service I use in this example is from GeoNames.org. Their web service, described here, simply takes a “feedURL” parameter and returns for you a version of the XML with coordinates for the matches it finds.

For example, sending the BBC News World Edition Front Page feed which includes an item about heavy rains in Australia:

<item>

<title>Heavy rains hit parched Australia</title>

<link>http://news.bbc.co.uk/go/rss/-/2/hi/asia-pacific/6670333.stm</link>

<description>Farmers get welcome relief from a six-year drought, as the heaviest rain for years soaks south-eastern Australia.</description>

</item>

…to the GeoNames.org web services by using this format:

http://ws.geonames.org/rssToGeoRSS?feedUrl=http://news.bbc.co.uk/rss/newsonline_world_edition/front_page/rss.xml

…will result in a feed being returned which includes the updated item with GeoRSS point tags added:

<item>

<title>Heavy rains hit parched Australia</title>

<link>http://news.bbc.co.uk/go/rss/-/2/hi/asia-pacific/6670333.stm</link>

<description>Farmers get welcome relief from a six-year drought, as the heaviest rain for years soaks south-eastern Australia.</description>

<geo:lat xmlns:geo=”http://www.w3.org/2003/01/geo/wgs84_pos#”>-25.0</geo:lat>

<geo:long xmlns:geo=”http://www.w3.org/2003/01/geo/wgs84_pos#”>135.0</geo:long>

</item>

So I have now in this feed spatially referenced items, along with <TITLE>, <LINK>, and <DESCRIPTION> tags from which to generate map tips. Starting with a simple web map page, what I’ll do next is write some code to dynamically populate a graphics resource with “graphic feature elements” derived from the designated GeoRSS feed, and configure the map to generate map tips for the new data. Along the way I’ll also wire up a few AJAX callbacks to support the application communication.

First, I’ll set up a simple web map with a dropdown for a few GeoRSS feeds, a MapResourceManager configured with a basemap resource and a graphics resource, a Toolbar, and a MapTips control. Finally, I have also added a “BuddiedCallbackButton”. This control is based on Ryan Olson’s excellent CallbackButton post, and I added to the code to be able to pass a collection of values to the callback handler from associated buddy controls, allowing in this case the URL of the selected GeoRSS feed in the dropdown to be used.

Example application layout in Visual Studio

When the “Add” command is clicked, I’ll set up a graphics layer in the “GeoRSSFeed” graphics resource. As described in the online EDN documentation, there are two graphics layer types, ElementGraphicsLayers, and FeatureGraphicsLayers. I’ll choose to generate a FeatureGraphicsLayer since these are best suited for storing attributes with the elements, which I’ll need to configure the map tips with. Since I know the GeoRSS feeds in this example all contain <TITLE>, <LINK>, and <DESCRIPTION> tags, I’ll set up the graphics layer to also maintain these attributes.

Next, I’ll use the .NET System.XML assembly to read and parse the selected GeoRSS feed. For each item in the feed, I’ll try to find the <geo:lat> and <geo:lon> tags, as well as the <TITLE>, <LINK>, and <DESCRIPTION> tags and store these in a StringDictionary.

As long as a coordinate was found for the item, I’ll add a new graphic element to the graphics layer and populate its attributes from the StringDictionary that was populated from the feed items.

Here’s the main code block to read the GeoRSS feed, parse for coordinates and attributes, and populate a graphics layer from the results:

    //note that the application could expose different values for the URL and maptips format tag:

    string sURL = “http://earthquake.usgs.gov/eqcenter/recenteqsww/catalogs/eqs1day-M2.5.xml”;

    string sBodyFormat = “Earthquake:<BR>{DESCRIPTION}<BR> <a target=_new href= {LINK} > {LINK} </A> <BR> <img src=’http://earthquake.usgs.gov/te
mplate/images/header_graphic_usgsIdentifier_white.jpg’>”
;

    #region load RSS feed

    XmlDocument _doc = new XmlDocument();

    String UrlString;

    //Build RSS Request String

    UrlString = sURL;

    //Load RSS document

    _doc.Load(UrlString);

    //Navigator and Iterator used to parse RSS

    System.Xml.XPath.XPathNavigator nav = _doc.CreateNavigator();

    System.Xml.XPath.XPathNodeIterator nodeItrItems = nav.Select(“//item”);

    int iCount = 0;

    //Loop through each item witin RSS

    while (nodeItrItems.MoveNext())

    {

      StringDictionary pValues = new StringDictionary();

      System.Xml.XPath.XPathNodeIterator nodeItrChildren = nodeItrItems.Current.SelectChildren(System.Xml.XPath.XPathNodeType.Element);

      ESRI.ArcGIS.ADF.Web.Geometry.Point pIncident = new ESRI.ArcGIS.ADF.Web.Geometry.Point();

      pIncident.Z = 100;

      ESRI.ArcGIS.ADF.Web.Display.Symbol.SimpleMarkerSymbol pSymbol = new ESRI.ArcGIS.ADF.Web.Display.Symbol.SimpleMarkerSymbol();

      ESRI.ArcGIS.ADF.Web.Display.Symbol.TextMarkerSymbol pText = new ESRI.ArcGIS.ADF.Web.Display.Symbol.TextMarkerSymbol();

      System.Drawing.Color pColor = new System.Drawing.Color();

      pColor = System.Drawing.Color.Red;

      pSymbol.Type = ESRI.ArcGIS.ADF.Web.Display.Symbol.MarkerSymbolType.Star;

      pSymbol.Width = 20;

      pSymbol.Color = pColor;

      //Loop through each child node and extract information

      while (nodeItrChildren.MoveNext())

      {

        string str = nodeItrChildren.Current.Name;

       
if (str == “geo:lat” || str == “LATITUDE”)

        {

          pIncident.Y = Convert.ToDouble(nodeItrChildren.Current.InnerXml);

          pIncident.Z = 0;

        }

        else if (str == “geo:long” || str == “LONGITUDE”)

        {

          pIncident.X = Convert.ToDouble(nodeItrChildren.Current.InnerXml);

          pIncident.Z = 0;

        }

        else if (pFields.Contains(str.ToUpper()))

        {

          string s = nodeItrChildren.Current.InnerXml;

          s = HttpUtility.HtmlDecode(s);

          pValues.Add(str.ToUpper(), s);

        }

      }

      if ((pIncident.Z != 100))//just using Z = 100 as code for unlocated

      {

        DataRow row = glayer.Add(pIncident);

        for (int ii = 0; ii < pFields.Count; ii++)

        {

          row[pFields[ii].ToString()] = pValues[pFields[ii].ToString()].ToString();

        }

        iCount++;

      }

      if (iCount >= 100)//stop after loading 100 feed items

      {

        break;

      }

    }

    #endregion

At this point, I have a graphics layer populated with point elements derived from the specified GeoRSS feed. I need to now configure the MapTips control to use this layer:

 

 #region update MapTips

    MapTips1.Features = null;

    MapTips1.Layer = “MapResourceManager1::GeoRSSFeed::GeoRSS Results”;

    MapTips1.Map = this.Map1.ID;

    MapTips1.FeatureLimit = 100;

    MapTips1.HeaderFormatString = “{TITLE}”;

    MapTips1.BodyFormatString = sBodyFormat;

    MapTips1.Refresh();

    #endregion

A very useful property of the graphics layer is that we can ask it for its geographic bounding box. Since I want to automatically zoom the map to this extent, I can use this property instead of having to write code to loop through all the coordinates of the feed items which were added.

Finally, I’ll add the MapTips and Map controls’ CallbackResults collections to the CallbackButton. In these callback results collections will be a lot of instructions to the processCallbackResult ESRI javascript handler on how to generate MapTips for the graphics layer and what in the map needs updating.

Since the MapTips’ BodyFormat property will honor both fields from the associated layer, as well as standard HTML formatting tags, we can mix and match these to come up with some nicely formatted MapTip pop-up windows when a feed point is clicked.

switch (sURL)

    {

      //USGS Recent Earthquakes:

      case “http://earthquake.usgs.gov/eqcenter/recenteqsww/catalogs/eqs1day-M2.5.xml”:

        sBodyFormat = “Earthquake:<BR>{DESCRIPTION}<BR> <a target=_new href= {LINK} > {LINK} </A> <BR> <img src=’http://earthquake.usgs.gov/template/images/header_graphic_usgsIdentifier_white.jpg’>”;

        break;

      //Google News Headlines:

      case “http://ws.geonames.org/rssToGeoRSS?feedUrl=http://news.google.com/nwshp?output=rss”:

        sBodyFormat = “{DESCRIPTION}”;

        break;

      //BBC World News:

      case “http://ws.geonames.org/rssToGeoRSS?feedUrl=http://news.bbc.co.uk/rss/newsonline_world_edition/front_page/rss.xml”:

        sBodyFormat = “{TITLE}:<BR><a target=_new href= {LINK} > {LINK} </A> <BR>”;

        break;

    }

News item from a map tips layer linked to a GeoRSS feed 

So that’s about it. Using web-tier graphics layers in the ArcGIS Server ADF can be a great way of adding dynamic data to your web map application, whether it be from existing XML-based internet feeds, or your own custom data generated by your application.

Posted in Services | Tagged , , , | 5 Comments

Determining layer IDs at runtime

In this post, Rex Hansen provides a tip for determining the ID for a given layer: 

The SearchAttibutesTask and QueryAttributesTask controls are designed to use a layer ID to define the layer within a resource on which a query will execute. When both tasks are configured at design-time the layer ID is stored in the definition of the query. The layer IDs are determined by the order of the layers in a resource (i.e. map service).

The layer order can change when existing layers are reordered or removed, or when new layers are added. In this situation, the layer IDs defined when you configured the SearchAttibutesTask and QueryAttributesTask become invalid. However, if the layer names will remain constant and unique you can discover the layer ID at runtime and update the query definition for both tasks. Although this pattern will add some overhead to each request, the layer ID will be dynamically updated without having to manually change and recompile the web application. The following code provides an example of this technique.

    protected void Page_Load(object sender, EventArgs e)

    {

        ESRI.ArcGIS.ADF.Web.DataSources.IGISFunctionality gisfunctionality = 

            Map1.GetFunctionality("MapResourceItem0");

 

        ESRI.ArcGIS.ADF.Web.DataSources.IMapFunctionality mf = 

            (ESRI.ArcGIS.ADF.Web.DataSources.IMapFunctionality)gisfunctionality;

 

        string[] layerids;

        string[] layernames;

        mf.GetLayers(out layerids, out layernames);

 

        for (int i = 0; i < layerids.Length; i++)

        {

            string layername = layernames[i];

            if (layername == "counties")

            {

                SearchAttributesTask1.SearchFields = 

                    string.Format("MapResourceManager1:::MapResourceItem0:::{0}:::NAME", layerids[i]);

                QueryAttributesTask1.PredefinedQuery.LayerID = layerids[i];

            }

        }

    }

Posted in Services | Tagged , , , | 1 Comment

Passing URL parameters with the Web ADF

We received a question about whether you can pass parameters back and forth in a URL using the Web ADF (many folks have done this previously in their ArcIMS applications). The answer is yes, and there is a sample on the ESRI Developer Network (EDN) website that shows how to pass URL parameters in your Web mapping applications.

This sample was created by our team, so let us know if you have any questions about it.

Posted in Services | Tagged , , | 6 Comments

Creating a 3D globe cache from a 2D map cache

ArcGIS Online globeOne purpose of this blog is to share some of the things we’ve learned about our software by using it ourselves. The ArcGIS Online project uses the ArcGIS Server map and globe caching technology. In this post, we share a technique we’ve developed to create 3D caches for ArcGIS Online.

For performance and appearance benefits, many of the 3D globe caches on ArcGIS Online were created originally from 2D fused map caches. These 2D caches were overlaid on the globe and cached to make globe tiles. This technique improves the performance of the globe service because it yields only one cached layer. It also improves the appearance of the service because anti-aliasing effects in the 2D cache transfer to the 3D cache. This post lists the steps that you can follow to create a 3D cache from a 2D cache.

The instructions in this post are written for intermediate to advanced ArcGIS Server users. Before attempting this technique, it’s helpful to have some previous experience publishing ArcGIS Server services, creating map caches, and connecting to GIS services in ArcGlobe.

Create the 2D map cache

The first step in this process is to create the 2D cached map service. Start by using ArcMap to create an attractive map document that is designed for display at some pre-determined scale levels. You’ll need to select the scale levels you want cached and then apply the appropriate symbology for each scale level. You can find tips for this process in Planning a map cache.

Below is a list of scale levels used by ArcGIS Online 2D caches. These scale levels translate well to the fixed scale levels used by ArcGIS Explorer and ArcGlobe, and are strongly recommended if you plan on eventually creating a 3D cache from your 2D cache. The high-precision values of the scales prevent tiles from overlapping at the edge of the globe near the International Date Line. If your map does not have worldwide coverage, you do not have to enter all of the decimal places for these numbers, but it’s recommended that you stay close to these numbers for the best visual effects. You do not have to use all of the scales; you can use a sequential subset of them.

Recommended scales for your 2D cache

  • 147748799.285417 (To clarify, this corresponds to approximately 1:147,748,799 scale)
  • 73874399.6427087 (This corresponds to approximately 1:73,874,299)
  • 36937199.8213544 (Etc.)
  • 18468599.9106772
  • 9234299.95533859
  • 4617149.97766929
  • 2308574.98883465
  • 1154287.49441732
  • 577143.747208662
  • 288571.873604331
  • 144285.936802165
  • 72142.9684010827
  • 36071.4842005414
  • 18035.7421002707
  • 9017.87105013534
  • 4508.93552506767

Once you’ve finished designing and creating your map document, you should publish it as an ArcGIS Server map service and create a map cache. If using the above values, you can copy and paste them directly into the Generate Map Server Cache tool. This post does not focus on the details of publishing the 2D service and generating the cache, but ample instructions are available in the ArcGIS Server Help.

Configure a custom error for missing tiles

A previous post on this blog explained how you can configure your web server to return a blank or “No data available” image in areas where a client cannot find a cache tile. This is an important step when you’re generating a 3D cache from a 2D cache because the tile edges may not always line up between the two types of caches. The 3D cache generating process will perform better with a blank or “No data available” tile than it will when no tile is found. Follow the steps at this link to configure your web server to return the blank tile: Configuring your server to display a “Data not available” tile for empty map cache areas.

Create a globe document from the 2D service

Now that you have a 2D map service running, you can add it to an ArcGlobe document. The service will be draped over the globe in 3D. Follow these steps to create and appropriately configure the document:

  1. Start ArcGlobe
    Tip: If you’re using ArcGlobe in a remote desktop session, you may get better performance by resizing the application window to eliminate the globe portion of the display. You don’t need to see the globe during these steps.
  2. Remove the default layers named Continents and World Image.
  3. Click Tools > Options and click the Cache tab.
  4. Set the Cache path to be the same as the server cache directory where you want your globe cache to be created, with “GlobeCache” appended to the end (Example: \myServercacheGlobeCache) This is an application-level property, so you may want to make note of the original cache path and set this value back the next time you use ArcGlobe.
  5. Click OK to dismiss the options dialog, then click the Add Data button.
  6. Add the 2D map service that you created in the previous steps. Be sure to use an Internet connection to ArcGIS Server (not a local connection). ArcGlobe can display the 2D service draped over the globe.
  7. Right-click the map service layer and select Properties.
  8. In the Globe General tab, modify the Layer Name and Description as you like.
  9. Click the Cache tab
  10. If you’re creating a base map layer, such as satellite imagery or a street map, check “Use lossy spatial compression”. If you’re creating a transparent reference layer, such as boundaries or a road network, this compression is not necessary.
  11. If you are using the recommended ArcGIS Online scale levels listed above, change the minimum cell size to twice the calculated value. Leaving this unchanged will cause your cache to grow larger than necessary.
  12. Click OK to dismiss the dialog.
  13. Save the globe document and close ArcGlobe.

Create a globe service

Now you need to publish a globe service using the globe document you created in the steps above. You can create the service in ArcCatalog or Manager. Be sure to set the service properties to reference the appropriate cache directory (It’s not necessary to append “GlobeCache” to the name this time.)

The globe service you create should be pooled, with 2 – 4 instances per server object container (SOC) that will be employed in the cache-building process. You may need to adjust this number depending on memory and CPU constraints you observe during the caching process.

Note about number of service instances: The map service that you created earlier only needs to have about ¼ the number of instances that your globe service has at this point. This is because you’ve cached the map service already, so requests for its tiles can be generally satisfied by the virtual cache directory, without making a request to the GIS server. The globe service is going to need a lot of instances while caching. After the cache is generated you can reduce the number of globe service instances.

Generate a globe cache

Once the globe service is started, you can begin generating the cache. You must do this step in ArcCatalog. In the Caching tab of the globe service properties, you’ll find two options for generating the cache. The Generate button creates a cache for the entire globe service, while the Update option only caches an extent that you provide.

As you set the parameters for the caching tools, you’ll need to select the levels of detail that the cache will be created at. Set the From level to “Globe – 1:10000000″ even if your service does not have global coverage. This will not generate an unreasonably large cache because tiles that are outside the service extent will just be skipped by the cache generation process.

When selecting the To level of detail, use the table below which associates each ArcGIS Online scale level with the appropriate To level of detail you should use for your globe cache. For example, if your map cache’s closest scale level is 72142.968… choose “City – 1:9765″ from the To Level of Detail dropdown in the Generate Globe Server Cache tool. Ignore the scale given in the dropdown (in this example 1:9765).

Scale level Globe cache “To” level of detail
73874399.6427087 Globe
36937199.8213544 Continent
18468599.9106772 Countries
9234299.95533859 Country
4617149.97766929 States
2308574.98883465 State
1154287.49441732 Counties
577143.747208662 County
288571.873604331 Metropolitan Area
144285.936802165 Cities
72142.9684010827 City
36071.4842005414 Town
18035.7421002707 Neighborhood
9017.87105013534 City Blocks
4508.93552506767 City Block

When you’ve set all of the parameters for the caching tools, click OK and let the cache generation process do its work. When it completes, you’ll have a set of globe tiles that look like the tiles in your 2D map cache.

Create a disconnected service (optional)

At this point, you could leave your globe service running and users would see the new cache tiles. However, your map service would always need to be running in order for your globe service to start correctly. To avoid this dependence on the map service, you can do the following to create a disconnected globe service:

  1. Stop the globe service you created previously.
  2. Open ArcGlobe and create a new globe document.
  3. Remove the default layers named Continents and World Image.
  4. Click the Add Data button.
  5. Browse to the folder on disk that contains the globe cache you just created. This involves browsing to your server cache directory, opening the GlobeCache folder, and finding the folder that represents your new globe cache. (Tip: The folder name will probably contain a representation of the URL of your 2D service, for example: ESRI_ServerLyrLocalGlobeCache_http___myserver_arcgis_services_world_mapserver_Layers_world.)
  6. Inside the globe cache’s folder, you’ll see a file named cache.lyr. Add cache.lyr to your map.
  7. Save the globe document and close ArcGlobe.
  8. Use ArcGIS Server to publish the globe document as a globe service. You’ve created a disconnected globe service.

(Note: Creating a 3D cache from a 2D cache using the method described above adds temporary local cache files that can add up to cumbersome sizes during long caching jobs. If this occurs you can use the operating system tools to schedule a task that deletes the local cache periodically, such as every 10 minutes. The local cache is typically stored in C:Documents and Settings<SOC account name>Local SettingsTempesrimapcache.)

Do you have a question about this technique or a tip of your own you’d like to share? If so, please leave a comment.

-Sterling Quinn

Posted in Services | Tagged , , , , , | 1 Comment

Print Task Sample

In this post, Bryan Baker comments on a new sample you can use to add a Print task to your Web ADF applications: 

A sample print task for the Web ADF for the Microsoft .NET Framework is now available at the ESRI Developer Network website. This sample allows users to do simple printing of a map and task results. ESRI plans to include printing in the upcoming 9.3 version, but this sample should get you started with printing from the Web ADF. The sample works in the Web ADFs for both ArcGIS Server and ArcIMS.

The sample task prints all resources and services in the Map control, including graphics layers and scalebar. It merges the resources into a single image that the browser displays in a new window. If the user has generated task results, such as with a query or identify, the user may also choose to print those results as a table below the map. The sample also allows entering a custom title. It does not currently support printing other items in the website, such as the table of contents or overview map.

In the website, the Map may be zoomed to any location, and other map properties may be set, such as turning layers on/off or displaying highlights from task results. If other tasks, such as a QueryAttributes task, have been added, the print task displays a list of results from those tasks to print. From the Print task dialog, the user may set a print title and task results to print (if any have been generated in the website). Clicking Print in the dialog pops up a new browser window with the title, map and task results (if any). The user may then print the page with their usual printing methods, to any available printer.

Some additional highlights:

  • The user may choose print size, resolution and scale. Resolution (e.g., 300 dpi) is accomplished via standard HTML in the browser.
  • The administrator can configure properties of the task, such as the default print page title, the maximum print size, whether to allow printing task results, and whether to print the scale bar on the map.

Installing the sample Print Task

You can install and use the sample task as-is, or customize it for your own use. To install the task you need to:

  1. Make sure the Web ADF is already installed, with Service Pack 1 or higher applied.
  2. Unzip the downloaded file.
  3. Register the task with the .NET global assembly cache.
  4. (Optional) Add the task to the Visual Studio toolbox.
  5. (Optional) Add the task to Manager.

Details for these steps are in the instructions at the EDN website, and also in the Readme.doc file accompanying the sample. The sample includes the source code for the task, which can be customized. (Note: If you downloaded the EDN sample before the date of this post, you should revisit the EDN site for updated instructions for adding the task to Manager.)

The instructions on EDN include the path to the ArcGIS Server Manager. To add the task to the ArcIMS Web Manager, follow the same instructions, except add the appropriate entry into the Tasks.xml file for the ArcIMS Web Manager, which by default is in C:InetpubwwwrootArcIMSManagerApplicationsApp_Data.

If you install a service pack for the Web ADF, you will need to reinstall the task into Manager. Uninstalling the task must be done manually; see the readme document for details.

Adding the sample Print Task to a Website

After you install the task, you can add it to a Web ADF web page, using either Manager or Visual Studio. In Manager, you should see the Print task in the list of available tasks when you create or edit a website. Add it to the list of tasks in the website, then configure it (you must click Configure for any task, even if you just keep the default settings). The configuration settings allows you to set properties such as the maximum allowed size and resolution and default title for the print page. When you save the website, the print task should appear in the menu of tasks in the website.

The Print task appears in the left menu of the Web mapping application

From Visual Studio, add the Print task like other tasks, usually into a TaskManager. Set the Print task's results container to a TaskResults control on the page, using the "smart tag" in the upper right of the task. You can set other properties in the Properties window of Visual Studio. You can set the task's Visible property to false if desired, and make sure a Menu or TreeView control is buddied to the TaskManager so the user sees a list of tasks to open.

The task currently requires that the web page include a Map control. If no Map control is present, an error will occur. The code could be customized to allow for a page with only tasks, such as a query task, with no map. Also, please note that the task will not work with ArcGIS Online services (currently in beta), since those services currently deliver maps only as cached tiles (this task requests new map images for printing).

This task does not use the layout of any ArcGIS Server service in the website. If you want to print using an MXD-based layout, you might want to check out Ismael Chivite's sample posted at ArcScripts that prints layouts.

We'd welcome your comments on this sample. The download page for the sample includes a user comments section. We'll also post periodic updates to the sample based on feedback.

You can try out the task yourself by printing a map from this example web application.

Posted in Services | Tagged , , , , | 12 Comments

Map rotation support in the Web ADF at SP 2

Rex Hansen wrote this thorough post about the new support for map rotation in version 9.2, Service Pack 2 of the Web ADF for the Microsoft® .NET Framework:

ArcGIS Server map services support the ability to serve a rotated data frame (map). In 9.2 SP 2, the .NET Web ADF has been enhanced to support map rotation. This involved a number of changes in many sections of the Web ADF. Each section is discussed below:

Map control

The Map control has a new Rotation property that can be used to specify, in degrees, the rotation of resources in the Map. A value of NaN means that the rotation of the PrimaryMapResource will be applied; 0 will unrotate rotated resources and leave other resources alone; values greater or less than 0 will rotate resources, throwing an exception if unrotatable resources are present. Map rotation is supported for ArcGIS Server (both Internet and Local) and Web ADF Graphics data sources.

If the Map control has a rotation value greater or less than 0:

  1. The presence of non-rotatable resources will return an exception in the Web page at runtime. The exception will indicate which resource does not support rotation.
  2. The ContinuousCallback and EnableTileCaching properties on the Map control will be disabled.

If the Map Rotation property is NaN, the rotation of the PrimaryMapResource (PMR) can be used. Since a Web ADF Graphics resource cannot be a PMR, only a rotation value specified for a data frame in an ArcGIS Server map service can be used.

Map rotation can also be set on the fly at runtime. As long as the resources in the Map control support rotation, Map contents will be updated. Again, if a resource does not support rotation, an exception will returned in the Web page indicating which resource.

The following code snippet illustrates how to change the Rotation property on a Map control:

            Map1.Reset();

            Map1.Rotation = 45;

Rotating an ArcGIS Server cached map service requires that dynamic map images are generated – the cached map tiles will not be used. As a result, many of the performance benefits of a cached map service are negated.

Web ADF controls that work with the Map have also been updated to support rotation, such as the Navigation, Magnifier, and OverviewMap controls.

TransformationParams and RotationParams classes

Two new classes have been introduced to support parameters used when transforming and rotating map content: TransformationParams and RotationParams.

TransformationParams stores parameters needed to convert coordinates between screen and map units. The Map control has a GetTransformationParams() method to return a TransformationParams instance set up using Map properties. The TransformationDirection enumeration allows you to select which direction the transformation will take place: either screen to map units (ToMap) or map to screen units (ToScreen).

The RotationParams class stores parameters associated with a rotated map extent. The TransformationParams class uses the RotatedParams class to account for rotation when converting coordinates between screen and map units.

In most cases, you’ll use an instance of TransformationParams when converting geometry to and from screen and map units. This is discussed in more detail in the next section.

Geometry conversion

A set of static methods on the Web ADF Geometry, Point and Envelope classes (in ESRI.ArcGIS.ADF.Web.Geometry) used to convert to and from screen and map units have been overloaded to support map rotation. Previous methods of the same name in 9.2 final and 9.2 sp1 have been marked obsolete because they do not support rotated maps. Obsolete methods will still work with unrotated map content. The new overloads provide a more efficient pattern for transforming Web ADF geometry. A single TransformationParams instance can be created from scratch or retrieved from a Map control and used in multiple geometry conversion calls. TransformationDirection.ToMap should be used for ToMap_ methods while TransformationDirection.ToScreen should be used with ToScreen_ methods. The overloaded methods are listed below:

In the ESRI.ArcGIS.ADF.Web.Geometry.Geometry class (inherited by concrete Web ADF geometry classes):

TransformGeometryToScreen(ESRI.ArcGIS.ADF.Web.Geometry.Geometry,

  ESRI.ArcGIS.ADF.Web.Geometry.TransformationParams)

In the ESRI.ArcGIS.ADF.Web.Geometry.Point class:

ToMapPoint(ESRI.ArcGIS.ADF.Web.Geometry.Point, ESRI.ArcGIS.ADF.Web.Geometry.TransformationParams)
 

ToMapPoint(int, int, ESRI.ArcGIS.ADF.Web.Geometry.TransformationParams)

ToScreenPoint(ESRI.ArcGIS.ADF.Web.Geometry.TransformationParams)

In the ESRI.ArcGIS.ADF.Web.Geometry.Envelope class:

ToMapEnvelope(System.Drawing.Rectangle, ESRI.ArcGIS.ADF.Web.Geometry.TransformationParams)

ToScreenRectangle(ESRI.ArcGIS.ADF.Web.Geometry.TransformationParams)

The following code snippet illustrates how to convert from a point in screen units to a Web ADF Point object in map units:

ESRI.ArcGIS.ADF.Web.Geometry.Point adfPoint = ESRI.ArcGIS.ADF.Web.Geometry.Point.ToMapPoint(screenPoint.X, screenPoint.Y, Map1.GetTransformationParams(TransformationDirection.ToMap);

Manager

When building a Web application in Manager, the Map rotation property is set to a value of NaN and cannot be changed explicitly. Instead, the Primary Map Resource (PMR) can be used to define a rotation value. In Manager, the PMR is set in the Preview Layers dialog by selecting a resource in the drop down list under “Use coordinate system, rotation and extent of:”. If the PMR is rotated, you cannot use non-rotatable service types, such as ArcIMS and WMS, in the same map. Manager will provide an informative warning if this situation occurs. If you select a non-rotatable service as the PMR, the Web ADF will unrotate any rotated service so that it lines up with the non-rotatable service.

Posted in Services | Tagged , , | 4 Comments

Follow-up to Extending the QueryAttributes Task: Zooming to selected features

From Bryan Baker, a product engineer working on the .NET SDK: 

I wrote an earlier post that showed how to extend the QueryAttributes task so that all features are immediately highlighted. Several users have asked about also zooming to the selected features. I'll show that here, though keep in mind that the user can also zoom to the selected features by right-clicking on the node for the layer (Cities in the graphic at the top of the earlier post) and choosing to zoom to selected features.

It turns out that it's a little more difficult to zoom to the features than I originally thought, because the FullExtent property of the graphics layer is null for queries like this. Instead, we have to construct our own envelope around all the features by looping through them. This isn't that difficult, though of course it does require more processing.

I’ve included code below that does the zooming. This code should be added to near the bottom of the existing code in the earlier post. If for some reason you didn’t want to highlight all the features, you could omit or comment out the line in the loop that sets the selectedCol to true.

The code below first creates an envelope to use, then in the existing loop that selects each feature, it widens the envelope to surround each feature. Once it has the envelope, it gets a reference to the Map control so it can set the Map’s extent. This takes some work, since the task itself has no reference to the Map. We have to get the ID of the Map and then search the page’s control tree. Since the Map control could be nested within another control, such as in a FloatingPanel, we search for it recursively using a custom function (found at the bottom of this listing).

One more thing before we zoom the map: the task could be querying a point layer, and if only one point is found, the “envelope” around all features is a point. We can’t zoom to a point, so instead we set the envelope to a percentage of the full extent of the Map (five percent—this value is hard-coded here, and you can change it depending on tightly you want to zoom in this one-point case).

Finally, we’ve got an envelope that will work, and we set the Map to this extent, refresh the Map, and copy its CallbackResults to the task’s CallbackResults. This last step is necessary because a callback only works with one control (the task in this case), and we need to tell another control (the Map) to update its contents.

    ' Set up the items to hold the extent of all features

    Dim geom As ESRI.ArcGIS.ADF.Web.Geometry.Geometry

    Dim layerEnv As New _

       ESRI.ArcGIS.ADF.Web.Geometry.Envelope( _

       Double.MaxValue, Double.MaxValue, _

       Double.MinValue, Double.MinValue)

    ' Set each feature to selected (this loop is

    ' at the end the code in my previous blog post)

For Each row As DataRow In graphicsLayer.Rows

   row(selectedCol) = True

    ' Enlarge the overall envelope to

    '  include the current feature

   geom = graphicsLayer.GeometryFromRow(row)

   layerEnv.Union(geom)

Next

    ' If any records found, zoom to them

If graphicsLayer.Rows.Count > 0 Then

    ' Get a reference to the Map – have to search the Page since

    '  task itself has no direct reference to the Map

    ' (the task's TaskResults does have the ID of the map)

    Dim mapCtrl As Map = Nothing

   Dim taskResultsId As String =

      Me.TaskResultsContainers(0).Name

    Dim taskResults As TaskResults = _

       CType(FindControlRecursive( _

       Me.Page, taskResultsId), TaskResults)

   If Not TaskResults Is Nothing Then

      mapCtrl = CType(FindControlRecursive( _

         Me.Page, taskResults.Map), Map)

   End If

   If Not mapCtrl Is Nothing Then

    ' If only one point found, envelope will be a point

    '   – set to a percentage of the full extent

      If layerEnv.XMin = layerEnv.XMax AndAlso _

         layerEnv.YMin = layerEnv.YMax AndAlso _

         Not IsNothing(mapCtrl) Then

    ' Percentage of the full extent to use when zooming to point

    Dim zoomToPointPercentage As Integer = 5

    Dim NewWidth As Double = mapCtrl.GetFullExtent().Width _

       * (zoomToPointPercentage / 100)

    Dim NewHeight As Double = mapCtrl.GetFullExtent().Height _

       * (zoomToPointPercentage / 100)

         layerEnv.XMin -= NewWidth / 2

         layerEnv.XMax += NewWidth / 2

         layerEnv.YMin -= NewHeight / 2

         layerEnv.YMax += NewHeight / 2

      End If

    ' Now we can zoom the map to the extent of the features

      mapCtrl.Extent = layerEnv

      mapCtrl.Refresh()

    ' We have to tell the client to refresh, using CallbackResults

      Me.CallbackResults.CopyFrom(mapCtrl.CallbackResults)

   End If

End If

Below is the function called in the above code. This searches the page and its child controls for the control with the given ID. Put this after the end of the Execute method (End Sub), but inside the Class (before the End Class statement).

    ' Finds the control in the Page's control tree

    Public Function FindControlRecursive(ByVal root As _

       Control, ByVal id As String) As Control

        If root.ID = id Then

            Return root

        End If

        Dim c As Control

        For Each c In root.Controls

            Dim t As Control = FindControlRecursive(c, id)

            If Not t Is Nothing Then

                Return t

            End If

        Next

        Return Nothing

    End Function

If you add the code above to the custom task as outlined in the earlier blog post, it should automatically zoom to the extent of all features found by the task.

 

Posted in Services | Tagged , , | 8 Comments

Customizing the Web Editor task

 James Goodrich, a developer on the .NET Web ADF team, contributed this information about some Service Pack 2 enhancements to the Editor task:

 When the Editor task was released at version 9.2 of the Web ADF for the .NET Framework, a common question was “How can I customize the Editor task?”. Service Pack 2 provides an answer to this question. You can now customize the Editor task with custom tools and Editor Panels and we have added more events that allow you to hook into the Editor task.

In addition to these new customization options for developers, the Editor task has another key change at Service Pack 2 which it allows it to be configured with a pooled map service. You can edit non-versioned data using a pooled map service.

See the Editor Task control discussion in the Developer Help for samples and instructions.

Customized Editor Task with added tools

Posted in Services | Tagged , , , | 8 Comments

How to track pending tiles and display a busy indicator in a Web mapping application

Rex Hansen contributed this post about how to use some of the enhanced JavaScript in Service Pack 2 to track pending tiles and display a busy indicator (such as an animated “Loading” graphic) over the Web ADF’s Map control: 

As a Web ADF developer working in an asynchronous communication environment, it is often beneficial to provide an end user with some indication that a user action is being processed. Since most Web ADF applications are centered on working with a map, the ability of an end user to effectively interact with map contents is essential. The Web ADF has the ability to asynchronously retrieve map data from multiple sources and consolidate it in a single map control. In general, data sources often differ in the time it takes to respond to a request. Since the Web ADF Map control is capable of rendering map data as it is returned to the browser, it’s possible that some portion of data in the map is visible and accessible before another portion. In this case, it will likely be important to let the end user know when the map control has finished loading map data from any and all sources.

To support this capability, 9.2 service pack 2 includes an enhanced Web ADF JavaScript Map object. The JavaScript Map object has a set of “event handlers” on the pendingTiles property. The pendingTiles property references an array of map image tiles to be rendered. The array is updated when the map needs new image tiles based on the current extent. Events on the pendingTiles property are listed below:

Event Description
add_onRequestsPending Triggered when the number of items in the pendingTiles array changes from 0 to a higher value
add_onRequestsRemove Triggered when an item is removed from the pendingTiles array
add_onRequestsCompleted Triggered when the number of item in the pendingTiles array changes to 0

Use these handlers on the Map object’s pendingTiles property to register a JavaScript function with the event. For example:

map.pendingTiles.add_onRequestsPending(showBusyIndicator)

where map is the Map object and showBusyIndicator is a JavaScript function to call when the number of items in the pendingTiles array changes from 0 to a higher value.

The JavaScript function showBusyIndicator may appear as follows.

function showBusyIndicator(sender) {

            showLayer(“BusyIndicator”);

            if (sender!=null) {

                window.status = “Pending Tiles: “ + sender.pendingTiles.length;

            }

The argument to the function is a reference to the JavaScript Map object. This argument can be used to gain access to map properties, such as the number of map image tiles left in the pendingTiles array. In this example, the number of pending tiles is output to the browser window’s status bar. If the argument is null, the pendingTiles array contains 0 items. The Web ADF includes two convenient JavaScript functions to show or hide a layer (div) based on its id, named showLayer and hideLayer, respectively. The functions are contained in the display_common.js file which is by default embedded with the Web ADF controls. In this example, the showLayer function is used to make the contents in the div tag with an id of “BusyIndicator” visible.

You can show the number of pending tiles and a "busy indicator" in a Web mapping application
 

Included below is a simple Web page with a MapResourceManager, Map, and a div tag containing an image. The JavaScript Map object events are handled after the form to let the content of the form load before interacting with it.
 

<%@ Page Language=”C#” AutoEventWireup=”true”  CodeFile=”Default.aspx.cs” Inherits=”_Default” %>

<%@ Register Assembly=”ESRI.ArcGIS.ADF.Web.UI.WebControls, Version=9.2.2.1350, Culture=neutral, PublicKeyToken=8fc3cc631e44ad86″

    Namespace=”ESRI.ArcGIS.ADF.Web.UI.WebControls” TagPrefix=”esri” %>

<!DOCTYPE html PUBLIC “-//W3C//DTD XHTML 1.0 Transitional//EN” “http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd”>

<html xmlns=”http://www.w3.org/1999/xhtml” >

<head runat=”server”>

    <title>Untitled Page</title>

</head>

<body>

    <form id=”form1″ runat=”server”>

    <div>

        <esri:MapResourceManager ID=”MapResourceManager1″ runat=”server”>

            <ResourceItems>

            </ResourceItems>

        </esri:MapResourceManager>

        <esri:Map ID=”Map1″ runat=”server” Height=”453px” Width=”556px” MapResourceManager=”MapResourceManager1″>

        </esri:Map>   

    </div>        

    

     <div id=”BusyIndicator” style=”z-index: 1000; left: 25px; width: 100px; position: absolute; top: 422px;height: 100px”>

        <img src=”images/CircleThickbox.gif” />

     </div>

   </form>

   <script language=”javascript” type=”text/javascript”>

           

        function showBusyIndicator(sender) {

            showLayer(“BusyIndicator”);

            if (sender!=null) {

                window.status = “Pending Tiles: “ + sender.pendingTiles.length;

            } 

        }

       

        function showPendingTiles(sender) {

            if (sender!=null) {

                window.status = “Pending Tiles: “ + sender.pendingTiles.length;

            } 

        }

       

        function hideBusyIndicator(sender) {

            hideLayer(“BusyIndicator”);

            if (sender!=null) {

                window.status = “”;

            } 

        }

       

        // add busy indicator functions to the map

        map = Maps["Map1"];

        map.pendingTiles.add_onRequestsPending(showBusyIndicator);

        map.pendingTiles.add_onRequestsRemove(showPendingTiles);

        map.pendingTiles.add_onRequestsCompleted(hideBusyIndicator);   

       

   </script>

</body>

</html>

Posted in Services | Tagged , , | 6 Comments

Using ASP.NET AJAX with the Web ADF

Bryan Baker of the .NET SDK team wrote this great article on integration ASP.NET AJAX with the Web ADF.   

 

Microsoft’s new ASP.NET AJAX enables developers to refresh portions of a Web page in a relatively simple way. To enable a control to use AJAX, the developer can put the control inside an UpdatePanel. Then any events on the control are automatically handled via AJAX, and updates are passed back without refreshing the entire page. For example, a page might have two drop-down lists. The first lists types of features, such as Cities and Countries. When the user picks a value in the first drop-down, the second list is populated with new values corresponding to the first list’s value, such as a list of cities.

Web ADF developers might want to use this approach to update the Map or other ADF controls. For example, when the user selects a city in the second drop-down list, the Map would zoom to the selected city. The problem is that ASP.NET AJAX was released after the 9.2 version of the Web ADF, and these new features were not available to be incorporated. Putting Web ADF controls inside an UpdatePanel will not work correctly, and is not supported. At 9.3 ESRI does plan to support Web ADF controls with ASP.NET AJAX.

You can have both Web ADF and ASP.NET AJAX controls on the page. The challenge is doing something like the example above, where controls in an UpdatePanel communicate with Web ADF controls. This post shows one approach for this. It turns out that it takes less than a dozen lines of code beyond what would have been needed had the Map been inside the UpdatePanel! You can follow along if you have installed Visual Studio 2005, the Web ADF, and the ASP.NET AJAX 1.0 extensions.

Note that the use of ASP.NET AJAX is not supported by ESRI for version 9.2 of the Web ADF. If you use ASP.NET AJAX, you must be prepared to resolve any issues you encounter with using the Web ADF. You may find help at the ESRI Forums, but ESRI Support will not be able to assist with development issues.

Add the controls

I first created a new website using the “ASP.NET AJAX-Enabled Web Site” template in Visual Studio. This creates a website and adds some extra items to support AJAX into both the Default.aspx page and the web.config file. You’ll notice the ScriptManager control already added to the Default.aspx page, which is a non-visual control that handles the AJAX functionality. If you wanted to add ASP.NET AJAX into an existing page or website, you’d need to add these same items that the template adds in. See the ASP.NET AJAX documentation for details.

ASP.NET AJAX and Web ADF controls on the page On the Default.aspx page, I added an UpdatePanel from the AJAX Extensions toolbox tab. I dragged two standard DropDownList controls into the UpdatePanel. The first DropDownList will display a list of layers. We could obtain these from the Web ADF controls on page startup, but I just added them manually for now to the Items property. I added two items: Cities and Countries. I also set the AutoPostBack property for both DropDownList controls to True. We need to do this to trigger a postback (actually a “partial postback” as ASP.NET AJAX calls it) when the user changes the selection.

Next I added a Map control and a MapResourceManager control from the ArcGIS Web Controls. Tip: having a drop-down list just above the map inteferes with the drop-down’s functioning, so I put the map above the drop-down lists for this demo. I set the MapResourceManager property of the Map, and I added a resource item (map service) to the MapResourceManager as required to enable the map to display the service. In my case I used a world map with cities and countries. My simple page looks like the example here.

Update DropDownList items with ASP.NET AJAX

When the user changes the first drop-down list, the second list should display a list of cities or countries. Using ASP.NET AJAX allows us to treat this like a standard postback event. Behind the scenes, ASP.NET AJAX handles the request using AJAX methods rather than a full postback. Fortunately we don’t have to deal with those details here.

I double-clicked on the DropDownList1 on the design page, which creates the method to handle the user selection in the code-behind page. We need to also fill the second DropDownList at startup, so we’ll create a separate method and call it from both the drop-down’s change method and the Page load method (which we can create by double-clicking on the design page). The UpdatePlaceList method fills the second DropDownList with locations and coordinates that we’ll use later for zooming the map. The code below is in VB; both C# and VB versions are available in the download link at the end of this post.

Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load

If Not Page.IsPostBack Then

UpdatePlaceList()

End If

End Sub





Protected Sub DropDownList1_SelectedIndexChanged(ByVal sender As Object, ByVal e As System.EventArgs)

UpdatePlaceList()

End Sub



Sub UpdatePlaceList()

DropDownList2.Items.Clear()

If DropDownList1.SelectedValue = "Cities" Then

DropDownList2.Items.Add(New ListItem("Cape Town", "17|-36|21|-32"))

DropDownList2.Items.Add(New ListItem("Oslo", "9|58|13|62"))

DropDownList2.Items.Add(New ListItem("Washington", "-79|37|-75|41"))

ElseIf DropDownList1.SelectedValue = "Countries" Then

DropDownList2.Items.Add(New ListItem("Australia", "112|-43|157|-10"))

DropDownList2.Items.Add(New ListItem("Brazil", "-73|-33|-31|6"))

DropDownList2.Items.Add(New ListItem("China", "75|16|136|55"))

End If



End Sub



Zoom the Map when user selects a place

When the user selects a place in the second DropDownList, we need to zoom the map to the coordinates of that place. The challenge is to have the map’s new extent be communicated to the client. Since the Map control cannot be in the UpdatePanel, we have to use another approach to communicate the results of the extent change to the Map.

The user selection in DropDownList2 triggers another partial postback. To set up the code for this, I double-clicked on DropDownList2 in design mode to create the method to handle the selected-index change. Inside this new method, I obtained the coordinates to zoom to from the DropDownList2.SelectedValue property. I parsed this value into an array of coordinate values, created a new envelope, and set the envelope’s extent to these coordinates. I then set the Map’s extent to this new envelope. This changes the map extent on the server. However, the client won’t be aware of the change and won’t obtain a new map unless we tell it to.

To get the client to update the map, we use a feature in ASP.NET AJAX to pass information to the client. The ScriptManager.RegisterDataItem method adds information that will be passed back and evaluated on the client.

The Map’s CallbackResults will have the information required to update the map on the client. We pass this information to the client using the RegisterDataItem method. We’ll see shortly how the client uses this information to update the map.

The code below obtains the callback results and registers them with the ASP.NET AJAX RegisterDataItem method. The ScriptManager1.IsInAsyncPostBack property ensures that we’re doing an AJAX partial postback rather than a full page postback.

Protected Sub DropDownList2_SelectedIndexChanged(ByVal sender As Object, ByVal e As System.EventArgs)



Dim zoomString As String = DropDownList2.SelectedValue

Dim coordArr() As String = zoomString.Split("|")



Dim new_extent As New ESRI.ArcGIS.ADF.Web.Geometry.Envelope

new_extent.XMin = Double.Parse(coordArr(0))

new_extent.YMin = Double.Parse(coordArr(1))

new_extent.XMax = Double.Parse(coordArr(2))

new_extent.YMax = Double.Parse(coordArr(3))

Map1.Extent = new_extent



If ScriptManager1.IsInAsyncPostBack Then

Dim callbackString As String = Map1.CallbackResults.ToString()

ScriptManager1.RegisterDataItem(Map1, callbackString)

End If



End Sub



Handle the results on the client

The results that we just registered with ScriptManager need to be handled on the client. ASP.NET AJAX has a handler approach to process items passed back to the client. I inserted the code below into the .aspx page, at a point below the asp:ScriptManager tag. This code registers a client-side handler, the PageLoadingHandler function, that will run when the page loads. This handler obtains the items registered on the server with RegisterDataItem, and checks whether any items for the map control are present.  Since our server-side code added an item for the map, the code runs the next line.

This next line calls processCallbackResult and passes it the callback results we registered on the server. The processCallbackResult function is part of the Web ADF JavaScript library, which is automatically downloaded when the page uses Web ADF controls. The processCallbackResult function applies the callback results on the client by retrieving a new map. One caveat: the objects and methods in the client-side library can change, so this method could change with future versions of the Web ADF.

<asp:ScriptManager ID="ScriptManager1" runat="server" />



<script type="text/javascript" language="javascript">



Sys.WebForms.PageRequestManager.getInstance().add_pageLoading(PageLoadingHandler);





function PageLoadingHandler(sender, args) {

var dataItems = args.get_dataItems();

if (dataItems['Map1'] != null)

processCallbackResult(dataItems['Map1'], 'Map1');

}



</script>



With this code in place, when the user selects an item in the places drop-down list, the server sets the map extent to that place, and the map on the client gets notified to retrieve a new map.

Tip: be careful about dragging controls around the page in Design mode when script blocks are within the page body. Doing so caused Visual Studio to remove my script block, which disabled the map update since no handler was available on the client!

Conclusion

We’ve seen how you can use controls in an ASP.NET AJAX UpdatePanel to control a Map in the Web ADF. Although it takes more work than if the Web ADF controls were embedded in the UpdatePanel, it is possible to do these tasks now. In our case it required less than a dozen lines of additional code compared to when the controls are all inside the UpdatePanel.

Download the code for this demo (includes C# and VB)

Try it out here

Posted in Services | Tagged , | 3 Comments