实现zoom to the selected feature之二:Extending the QueryAttributesTask to highlight selected features

Extending the QueryAttributesTask to highlight selected features

Bryan Baker of the .NET SDK team wrote the following great post on extending a task to modify its behavior and then adding that task to the .NET Global Assembly Cache so it can be reused across applications. 

Note: Also see the follow-up to this post from May 3, 2007.

Highlighting all task results

 

With the Web ADF at 9.2, a website can have one or more tasks to allow users to find features or locations on the map. For example, you might add a QueryAttributesTask to the website to allow users to find cities by name, by typing the first few characters in the name.

 

The out-of-the-box tasks at 9.2 do not automatically highlight found features on the map. Instead, the user can highlight features by clicking individual check-boxes of features in the task results (see graphic, with two cities selected).

 

 

What if you want to have all features highlighted immediately when the task runs? There is currently no setting to enable this. But such immediate highlighting is possible through customization. Let’s look at one relatively easy approach for someone with modest programming skills. We’re looking specifically at the Web ADF for the Microsoft .NET Framework here, by the way.

 

We will extend a task control to modify its behavior. The object-oriented nature of .NET allows us to create a new class (a task, in this case) that inherits all of the behavior and properties of the original class. We only need to add or modify the original class where we need it to act differently from the original class (task). Some aspects of tasks may be difficult or impossible to change, but modifying the task results output is not difficult.

 

The approach we’ll look at can apply to any task that produces a task result in the form of a graphics layer, where users can click on feature check-boxes to highlight them on the map. This includes the SearchAttributesTask, FindAddressTask, FindPlaceTask and QueryAttributesTask. We’ll look specifically at the QueryAttributesTask in this example.

Extending an out-of-the-box task

 

First, I open Visual Studio 2005 and create a new project (not a new website), a Class Library project to be specific. One great thing about .NET is that even though the original class was written in C#, we can extend it in any language—we’ll use VB to show this here. We can put our new class library anywhere; it doesn’t have to go into a web folder. I’ll call my project QuerySelectTaskVB. By the way, we could also use either Visual Basic Express or Visual C# Express, but it’s more difficult to debug with a linked web application.

 

Visual Studio creates the project and adds a new class called Class1.vb. I right-clicked on it in the Solution Explorer and renamed it QuerySelectTaskVB. This also renames the class in the code view—nice.

 

We need to add some references to the project to the libraries we’ll be using. I right-click on the project in Solution Explorer, and chose Add Reference, and in the pop up dialog I select these libraries and then click OK:

  • ESRI.ArcGIS.ADF.Tasks
  • ESRI.ArcGIS.ADF.Web
  • ESRI.ArcGIS.ADF.Web.DataSources
  • ESRI.ArcGIS.ADF.Web.UI.WebControls
  • System.Web

 

To start creating our class, we add some Imports statements at the top of the class file so we can use classes without having to type the full path. We’ll also add a Namespace around our class to better identify it.

 

Imports System
Imports System.ComponentModel
Imports System.ComponentModel.Design
Imports System.Data
Imports System.Web
imports System.Web.UI
Imports ESRI.ArcGIS.ADF.Tasks
Imports ESRI.ArcGIS.ADF.Web.Display.Graphics
Imports ESRI.ArcGIS.ADF.Web.UI.WebControls

 

Namespace QuerySelect
 Public Class QuerySelectTaskVB
 End Class
End Namespace

 

We tell the class to extend (inherit) the existing QueryAttributesTask by adding to the class declaration we’ve already created:

 

Public Class QuerySelectTaskVB
    Inherits QueryAttributesTask

End Class

 

The only thing we want to change in the task is how it outputs the results to the TaskResults control. The task creates this output in the standard task method called ExecuteTask(). We want the QueryAttributesTask to create its output as usual, but we will modify the results once they’re created. So we create our own version of ExecuteTask() that overrides the original. Our version will call the base (parent) class version of the method, then modify the results that have been created. The following code goes inside the Class definition we saw above:

 

    Public Overrides Sub ExecuteTask()
        ' QueryAttributesTask creates its results
        MyBase.ExecuteTask()
        ' We'll modify the Results next
    End Sub

 

Once the parent class has run its ExecuteTask, we can modify the results. The results are stored in a task property object called Results (makes sense, eh?). If the query finds features, those get stored in Results as a standard .NET DataSet, which contains one or more DataTable objects. But if nothing was found, the Results contains a different type, a SimpleTaskResult. Also, if the map service isn’t working right, the results might have a DataSet, but the name (caption) just has an error message. Let’s make sure we have valid results:

 

        ' We'll modify the Results next
        ' Make sure features were found

        If TypeOf Results Is DataSet Then

            Dim resultsDS As DataSet = CType(Results, DataSet)
            ' Check for errors during query

            If resultsDS.DataSetName.IndexOf("Error") > 0 Then
               
Return
           
End If

             ' Next we can get the table of results
       
End If

 

Now we can get the table from the DataSet. With the QueryAttributesTask, only one DataTable will be in the DataSet (other tasks may have multiple tables). One more error check: after the task performs its query using the Web ADF common API, it converts the data table to a GraphicsLayer object, so that it contains rendering (symbology) information. If it had any problems making that conversion, then features can’t be selected on the map.

 

            ' Next we can get the table of results
           
' Get the one table in the QueryAttributesTask result
           
Dim resultsTable As DataTable = resultsDS.Tables(0)
 

            ' Make sure no problems creating a GraphicsLayer from the results
           
If resultsTable Is Nothing OrElse Not TypeOf resultsTable Is GraphicsLayer Then

                Return

            End If

            ' Now we can modify the table results

 

Now we can actually set the selection for the features found! The GraphicsLayer, which extends the DataTable type, will have a Boolean-type column indicating whether the row (map feature) is selected. We can get this column, and use it when looping through the features to set all features as selected:

 

            ' Now we can modify the table results
           
Dim graphicsLayer As GraphicsLayer = _
              CType
(resultsTable, GraphicsLayer)
 

            ' Get the column that holds the selection attribute
           
Dim selectedCol As DataColumn = graphicsLayer.IsSelectedColumn
 

            ' Set each feature to selected
           
For Each row As DataRow In graphicsLayer.Rows
                row(selectedCol) = True

            Next

 

That’s it! Now each feature will be selected in the TaskResults tree and also on the map….that is, once our task is inside a website, which we’ll show shortly.

 

We compile the code by choosing Build—Build Solution from the menu (no errors, of course!). This puts a compiled .dll file into the Bin directory of the project (you can see that by looking at the project folder with Windows Explorer).

 

Adding the task to Visual Studio

The easiest way to add the task to a website is when it’s in the Visual Studio toolbox. It can be added manually to an individual website, but requires more editing of the source of the page. Let’s get the task into Visual Studio. There’s more on this in the Developer Help for ArcGIS Server and ArcIMS, so we’ll go over this quickly.

 

First, in the code for the task itself, we add some information so Visual Studio sets properties for task when we drag it onto the page from the toolbox. This goes just above the class declaration:

 

<ToolboxData("<{0}:QuerySelectTaskVB runat=""server"" Width=""200px"" Transparency=""35""" _

        + "BackColor=""White"" TitleBarColor=""WhiteSmoke"" TitleBarSeparatorLine=""False""" _

        + "TitleBarHeight=""20px"" BorderColor=""LightSteelBlue"" BorderStyle=''Outset''' _

        + "BorderWidth=""1px"" Font-Names=""Verdana"" Font-Size=""8pt"" ForeColor=""Black"">" _

        + "</{0}: QuerySelectTaskVB >")> _

Public Class QuerySelectTaskVB

    Inherits QueryAttributesTask

 

Next we will add the task to .NET’s Global Assembly Cache. This makes it easier to add the task to various websites. Before we can do this, we must create a strong name to sign the task. We open the .NET command prompt and type:

 

sn –k QuerySelectVB.snk

 

This creates a key file (QuerySelectVB.snk) in the folder indicated by the command prompt (probably C:\Program Files\Microsoft Visual Studio 8\SDK\v2.0). I moved that file using Windows Explorer to the project directory where my task .vb file is located. I then added that to my project by right-clicking on the project in Visual Studio’s Solution Explorer, choosing Add-Existing Item, changing the type to All Files, and clicking on the .snk file. I used the keypair to sign the assembly by right-clicking on the project and choosing Properties. In the Properties page, I clicked the Signing tab. I checked the box Sign the assembly and clicked my .snk file in the drop-down list. Finally, I recompiled the code (Build-Build Solution).

 

Now we can add the task to the Global Assembly Cache. Here’s the easy way: I opened two Windows Explorer windows, one to my project folder’s bin directory where the .dll is located, the other to C:\Windows\Assembly. I dragged the QuerySelectTaskVB.dll into the C:\Windows\Assembly window. This doesn’t actually move the file. It just registers the assembly so it’s available to all applications on the system.

 

Finally let’s add the task to Visual Studio. Open Visual Studio and then any .aspx page (you can create a website and page if necessary). Open the toolbox if necessary. Right-click where you want to add the task and click Choose Items (you can also create a new tab with New Tab). In the Choose Toolbox Items dialog, click Browse, and navigate to your DLL’s location. Highlight it and click Open. This adds the task to the list of available assemblies. Make sure its check box is checked, and click OK. This adds the task to the Visual Studio toolbox.

 

Now the task is available whenever you design an ASP.NET web page. Notice it has an icon—the same one as the QueryAttributesTask. It inherits this along with other properties of the parent task.

Using the custom task in a website

 

We can use the task in any website. I created a new website using the Web Mapping Application template. I could have added this website to the same solution as my task code if I’m using Visual Studio (useful for debugging the task), or in a new instance of Visual Studio. I could also open a website created with ArcGIS Server Manager or ArcIMS Web Manager, and add the task to it.

 

Once we have our web page open in Visual Studio, we can drag our new task from the toolbox into the Task Manager. Once it’s there, we can click its “smart tag” in its upper right to set the task’s properties.

 

 

 

Yet another great thing about extending an existing task: we get its designer tools with no coding required. We can set the Task Results container and use Edit the Query to create the query, exactly the same as with the standard QueryAttributesTask (see the Developer Help for tips on that).

 

The demo site for this page uses this custom task to query a water-well point layer for estimated yield. Choose a value from the drop-down list, and up to 50 features will be found. Notice that all features are immediately highlighted. Our custom task acts like the standard QueryAttributesTask, except that it highlights all features returned.

 

Try it out: http://serverx.esri.com/QuerySelectTaskDemo/.

Discussion

 

You can do other interesting things with the Results of the task. For instance, you could add a hyperlink to the task results output by adding the <a href…> tag to the DataSet’s DataSetName property, or to the DataTable.TableName. The hyperlink is displayed in the tree of the task results. You can also modify the DataTable itself, such as by hiding columns you don’t want displayed. Another example would be to display the results as a table, by creating a GridView from the DataTable and getting the HTML output from the GridView. You’d then create a new TaskResultsNode, add the output to that, and set it as the Results instead of the DataSet.

 

You might be tempted to customize other aspects of tasks, such as modifying the task’s user interface. Although some customizations are possible, you’ll probably find that in many cases it would be easier to author your own task from scratch. The Developer Help has a good discussion of writing tasks, and the Web ADF has a couple of samples that can help get you started.

 

You might have noticed that the task user interface does not have the look-and-feel of other tasks—the colors, fonts, etc. The standard tasks have properties defined in the Theme of the website, specifically in the Default.skin file. If you want the same look-and-feel of the standard tasks, you can create your own control skin by copying properties from one of the standard tasks in Default.skin.

 

We haven’t discussed how to make our custom task available within Manager, so that non-programmers could use your task. This is a much more complex chore than adding the task to Visual Studio. Tasks are configured in Manager using a separate class called a web configurator. The standard task’s web configurators are hard-coded to output information specifically for that task. It is not possible to easily modify them to instead output tags and properties for tasks that extend them. It would be necessary to rewrite most or all of the web configurator in order for it to properly output your task from Manager. The Developer Help has information on writing web configurators, if you are ambitious.

 

Download the code for this sample:

<!--[if !supportLists]-->-         <!--[endif]-->QuerySelectTaskVB.zip (VB source code)

<!--[if !supportLists]-->-         <!--[endif]-->QuerySelectTaskCS.zip (C# source code)

 

 

 

Published Monday, February 12, 2007 9:50 AM by Jeremy
Filed under: ADF, .NET, Code Snippet, Tasks

Comments

New Comments to this post are disabled
posted @ 2007-07-12 14:25  四两  阅读(960)  评论(1编辑  收藏  举报
加油,哥们,现在开始!