How to create a Find task

An example of XAML and .NET code (in this case C#) for a simple WPF application that includes a Find task working with local data is shown below. This application defines a Find task that uses a TextBox control for specifying the input value and a button for executing the task. Result features are displayed in a GraphicLayer. The rest of this document will walk you through how the Find task is defined in the example including a section on working with online data.

<Window x:Class="ArcGISWpfApplication1.MainWindow"
     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:esri="http://schemas.esri.com/arcgis/client/2009"   >     
    <Grid x:Name="LayoutRoot" Background="White">
        <Grid.Resources>
	<esri:SimpleMarkerSymbol x:Key="ResultsSymbol" Color="Red"/>
</Grid.Resources>
        <!-- Map Control -->
        <esri:Map x:Name="MyMap">
            <esri:Map.Layers>
            <esri:ArcGISLocalDynamicMapServiceLayer ID="USA" Path="C:\Program Files (x86)\ArcGIS SDKs\WPF10.2.5\sdk\samples\data\mpks\USCitiesStates.mpk"/>
                <esri:GraphicsLayer ID="MyGraphicsLayer">                    
                </esri:GraphicsLayer>
            </esri:Map.Layers>
        </esri:Map>
        <Canvas HorizontalAlignment="Right" VerticalAlignment="Top" Margin="0,15,7,0" Width="230" >
            <Rectangle Fill="Green" Stroke="Gray"  RadiusX="10" RadiusY="10" Width="210" Height="55"/>
            <TextBlock Text="Find Cities with names containing:" Foreground="White" FontSize="10" Margin="10,5,0,0"/>
            <TextBox x:Name="FindTextBox" Width="150" Margin="15,22,0,0" Text="San"/>
            <Button x:Name="FindButton" Content="Find" Margin="168,23,0,0" Click="FindButton_Click" />
        </Canvas>
    </Grid>
</Window>

using System.Windows;
using ESRI.ArcGIS.Client.Local;
using ESRI.ArcGIS.Client;
using ESRI.ArcGIS.Client.Tasks;

namespace ArcGISWpfApplication1
{
    public partial class MainWindow : Window
    {
        FindTask findTask;     
    
        public MainWindow()
        {
            InitializeComponent();
            
            LocalMapService.GetServiceAsync(@"C:\Program Files (x86)\ArcGIS SDKs\WPF10.2.5\sdk\samples\data\mpks\USCitiesStates.mpk", ms =>
            {
                findTask = new FindTask(ms.UrlMapService);
                findTask.ExecuteCompleted += FindTask_ExecuteCompleted;
                findTask.Failed += FindTask_Failed;
            });
        }

        private void FindButton_Click(object sender, RoutedEventArgs e)
        {
            if(findTask == null)
                return;
     
             FindParameters findParameters = new FindParameters();
             findParameters.LayerIds.AddRange(new int[] { 0 });
             findParameters.SearchFields.AddRange(new string[] { "AREANAME" });
             findParameters.ReturnGeometry = true;
             findParameters.SearchText = FindTextBox.Text;
             findTask.ExecuteAsync(findParameters);
        }

        private void FindTask_ExecuteCompleted(object sender, FindEventArgs args)
        {
            GraphicsLayer graphicsLayer = MyMap.Layers["MyGraphicsLayer"] as GraphicsLayer;
            graphicsLayer.Renderer = new SimpleRenderer()
            {
                Symbol = LayoutRoot.Resources["ResultsSymbol"] as ESRI.ArcGIS.Client.Symbols.Symbol 
            };

            graphicsLayer.Graphics.Clear();

            List<Graphic> graphics = new List<Graphic>();

            if (args.FindResults.Count > 0)
            {
                foreach (FindResult result in args.FindResults)
                {
                    ESRI.ArcGIS.Client.Graphic graphic = new ESRI.ArcGIS.Client.Graphic()
                    {
                        Geometry = result.Feature.Geometry,
                    };
                    graphics.Add(graphic);                    
                }
                graphicsLayer.Graphics.AddRange(graphics);
            }
            else
            {
                MessageBox.Show("No features found");
            }
        }
        private void FindTask_Failed(object sender, TaskFailedEventArgs args)
        {
            MessageBox.Show("Find failed: " + args.Error);
        }
    }
}

The following steps assume you have created a WPF application with a local map service. The XAML view of your application's main window (e.g. MainWindow.xaml) should look similar to the following:

<Window x:Class="ArcGISWpfApplication1.MainWindow"
	xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
 xmlns:esri="http://schemas.esri.com/arcgis/client/2009"
 Title="MainWindow">
	<Grid>

		<!-- MAP -->
		<esri:Map x:Name="MyMap">
  <esri:ArcGISLocalDynamicMapServiceLayer ID="USA" Path="C:\Program Files (x86)\ArcGIS SDKs\WPF10.2.5\sdk\samples\data\mpks\USCitiesStates.mpk"/>
		</esri:Map>
	</Grid>
</Window>

Creating an input interface for the Find task

Since tasks do not define user interfaces, you need to implement an interface for the Find task's input to allow your application's users to execute find operations. For this, the example includes a TextBox for defining the input value and a Button to execute the task.

  1. In XAML, define a Canvas to hold the task's input interface.
    <Canvas HorizontalAlignment="Right" VerticalAlignment="Top" Margin="0,15,7,0" Width="230" >
    </Canvas>
    
  2. Specify a Rectangle to use as the background for the input interface.
    <Canvas HorizontalAlignment="Right" VerticalAlignment="Top" Margin="0,15,7,0" Width="230" >
    	<Rectangle Fill="Green" Stroke="Gray"  RadiusX="10" RadiusY="10" Width="210" Height="55" />
    </Canvas>
    
  3. Add a TextBlock to inform the user how to use the task.
    Canvas HorizontalAlignment="Right" VerticalAlignment="Top" Margin="0,15,7,0" Width="230" >
    	<Rectangle Fill="Green" Stroke="Gray"  RadiusX="10" RadiusY="10" Width="210" Height="55" />
    	<TextBlock Text="Find cities with names containing:" Foreground="White" FontSize="10" Margin="10,5,0,0" />
    </Canvas>
    
  4. Define a TextBox for specifying the task's input value.
    <Canvas HorizontalAlignment="Right" VerticalAlignment="Top" Margin="0,15,7,0" Width="230" >
    	<Rectangle Fill="Green" Stroke="Gray"  RadiusX="10" RadiusY="10" Width="210" Height="55" />
    	<TextBlock Text="Find cities with names containing:" Foreground="White" FontSize="10" Margin="10,5,0,0" />
    	<TextBox x:Name="FindTextBox" Width="150" Margin="15,22,0,0" />
    </Canvas>
    
  5. Add a default value to the TextBox.
    <Canvas HorizontalAlignment="Right" VerticalAlignment="Top" Margin="0,15,7,0" Width="230" >
    	<Rectangle Fill="Green" Stroke="Gray"  RadiusX="10" RadiusY="10" Width="210" Height="55" />
    	<TextBlock Text="Find cities with names containing:" Foreground="White" FontSize="10" Margin="10,5,0,0" />
    	<TextBox x:Name="FindTextBox" Width="150" Margin="15,22,0,0" Text="San Francisco" />
    </Canvas>
    
  6. Add a Button to execute the task.
    <Canvas HorizontalAlignment="Right" VerticalAlignment="Top" Margin="0,15,7,0" Width="230" >
    	<Rectangle Fill="Green" Stroke="Gray"  RadiusX="10" RadiusY="10" Width="210" Height="55" />
    	<TextBlock Text="Find cities with names containing:" Foreground="White" FontSize="10" Margin="10,5,0,0" />
    	<TextBox x:Name="FindTextBox" Width="150" Margin="15,22,0,0" Text="San Francisco" />
    	<Button x:Name="FindButton" Content="Find" Margin="168,23,0,0" />
    </Canvas>
    
  7. Specify a handler for the Button's Click event. Later, you will implement this handler so that it executes the Find task.
    <Canvas HorizontalAlignment="Right" VerticalAlignment="Top" Margin="0,15,7,0" Width="230" >
    	<Rectangle Fill="Green" Stroke="Gray"  RadiusX="10" RadiusY="10" Width="210" Height="55" />
    	<TextBlock Text="Find cities with names containing:" Foreground="White" FontSize="10" Margin="10,5,0,0" />
    	<TextBox x:Name="FindTextBox" Width="150" Margin="15,22,0,0" Text="San Francisco" />
    	<Button x:Name="FindButton" Content="Find" Margin="168,23,0,0" Click="FindButton_Click" />
    </Canvas>
    

Implementing the Find task's execution logic

Now that you've specified the Find task's user interface, you need to define its execution logic. This execution logic can be divided into three parts:

  • Task execution
  • Task results display
  • Execution error handling

You will implement these components in .NET code contained in the main window's code-behind. This code is linked to the XAML presentation layer by manipulating elements that you declared in XAML with "x:Name" or "ID" attributes and implementing methods that you declared in XAML as event handlers. The steps below assume that you are adding code to the Window class in the code-behind file for your WPF application's main window (e.g. MainWindow.xaml.cs). In this example, C# is used.

Executing the task

To execute a Find task, you need to instantiate the task, specifying the map service that will be searched, wire the task's event handlers, initialize the task's search parameters, and call the task's execution method. The steps below will show you how to do this in the code-behind of your application's main window (e.g. MainWindow.xaml.cs). The task is declared and initialized in the code-behind because tasks alone do not define any user interface, but rather encapsulate pieces of execution logic.

The code shown in these steps is written in C#.

  1. In the code-behind class of your application's main window, find the constructor for the application and implement the code to start the LocalMapService asynchronously and set the FindTask Url when the service has started. You will also specify a handler for the task's ExecuteCompleted event. The method specified will be called when the Find task is done executing. You will implement this handler in the "Displaying results" section. Also, specify a handler for the task's Failed event, which fires when there is a problem executing the task. You will define this handler in the "Handling execution errors" section.
    LocalMapService.GetServiceAsync(@"C:\Program Files (x86)\ArcGIS SDKs\WPF10.2.5\sdk\samples\data\mpks\USCitiesStates.mpk", ms =>
                {
                    findTask = new FindTask(ms.UrlMapService);
                    findTask.ExecuteCompleted += FindTask_ExecuteCompleted;
                    findTask.Failed += FindTask_Failed;
                });
    
  2. Next find the handler for the FindButton's click event. Recall that you declared this handler when you defined the FindButton control in the window's XAML. In the click handler, check the FindTask is instantiated. Note that another way to approach this would be to disable the Find button initially until the local service has started.
    private void FindButton_Click(object sender, RoutedEventArgs e)
    {
       if(findTask == null)
           return;
    }
    
  3. Declare and instantiate a FindParameters object. The FindParameters object is used to define the execution parameters for Find tasks.
    private void FindButton_Click(object sender, RoutedEventArgs e)
    {
     FindParameters findParameters = new FindParameters();
    }
    
  4. On the FindParameters object, set the layer to be searched by adding a layer ID of 0 to the LayerIds property. This corresponds to the cities layer.
    private void FindButton_Click(object sender, RoutedEventArgs e)
    {
     FindParameters findParameters = new FindParameters();
     findParameters.LayerIds.AddRange(new int[] { 0 });
    }
    
  5. Specify that the AREANAME field be searched in the find operation.
    private void FindButton_Click(object sender, RoutedEventArgs e)
    {
     FindParameters findParameters = new FindParameters();
     findParameters.LayerIds.AddRange(new int[] { 0 });
     findParameters.SearchFields.AddRange(new string[] { "AREANAME" });
    }
    
  6. Since you will draw the Find task's results on the map, specify that geometry be returned with the results.
    private void FindButton_Click(object sender, RoutedEventArgs e)
    {
     FindParameters findParameters = new FindParameters();
     findParameters.LayerIds.AddRange(new int[] { 0 });
     findParameters.SearchFields.AddRange(new string[] { "AREANAME" });
     findParameters.ReturnGeometry = true;
    }
    
  7. Define the value to search for as the text in the FindTextBox control.
    private void FindButton_Click(object sender, RoutedEventArgs e)
    {
     FindParameters findParameters = new FindParameters();
     findParameters.LayerIds.AddRange(new int[] { 0 });
     findParameters.SearchFields.AddRange(new string[] { "AREANAME" });
     findParameters.ReturnGeometry = true;
     findParameters.SearchText = FindTextBox.Text;
    }
    
  8. Execute the find task with the parameters specified by the FindParameters member variable.
    private void FindButton_Click(object sender, RoutedEventArgs e)
    {
     FindParameters findParameters = new FindParameters();
     findParameters.LayerIds.AddRange(new int[] { 0 });
     findParameters.SearchFields.AddRange(new string[] { "AREANAME" });
     findParameters.ReturnGeometry = true;
     findParameters.SearchText = FindTextBox.Text;
     findTask.ExecuteAsync(findParameters);
    }
    

Displaying results

  1. Declare a handler for the Find task's ExecuteCompleted event. This handler will be invoked when a find operation is complete. A list of FindResults containing information about the features with matching attributes is passed to the handler's args parameter. Each FindResult contains the feature found, the name and ID of the layer containing the feature, the name of the field containing the matching value, and other information.
    private void FindTask_ExecuteCompleted(object sender, FindEventArgs args)
    {
    }
    
  2. Get a reference to the results GraphicsLayer and clear any previously added graphics from it.
    private void FindTask_ExecuteCompleted(object sender, FindEventArgs args)
    {
    	GraphicsLayer graphicsLayer = MyMap.Layers["MyGraphicsLayer"] as GraphicsLayer;
    	graphicsLayer.Graphics.Clear();
    }
    
  3. Create a renderer for the GraphicsLayer and create an empty generic List collection to hold the new graphics before inserting them into the layer.
    private void FindTask_ExecuteCompleted(object sender, FindEventArgs args)
    {
    	GraphicsLayer graphicsLayer = MyMap.Layers["MyGraphicsLayer"] as GraphicsLayer;
    	graphicsLayer.Graphics.Clear();
    
     graphicsLayer.Renderer = new SimpleRenderer()
     {
         Symbol = LayoutRoot.Resources["ResultsSymbol"] as ESRI.ArcGIS.Client.Symbols.Symbol 
     };
    
     List<Graphic> graphics = new List<Graphic>();
    
    }
    
  4. If results were found, loop through them. Create a graphic for each result and set the geometry. Add each graphic to the List of graphics then call AddRange to add the results to the GraphicsLayer.
    private void FindTask_ExecuteCompleted(object sender, FindEventArgs args)
    {
    	GraphicsLayer graphicsLayer = MyMap.Layers["MyGraphicsLayer"] as GraphicsLayer;
    	graphicsLayer.Graphics.Clear();
    
    	 graphicsLayer.Renderer = new SimpleRenderer()
      {
          Symbol = LayoutRoot.Resources["ResultsSymbol"] as ESRI.ArcGIS.Client.Symbols.Symbol 
      };
    
      graphicsLayer.Graphics.Clear();
    
      List<Graphic> graphics = new List<Graphic>();
    
      if (args.FindResults.Count > 0)
      {
          foreach (FindResult result in args.FindResults)
          {
              ESRI.ArcGIS.Client.Graphic graphic = new ESRI.ArcGIS.Client.Graphic()
              {
                  Geometry = result.Feature.Geometry,
              };
              graphics.Add(graphic);                    
          }
          graphicsLayer.Graphics.AddRange(graphics);
      }
    	else
    	{
    	}
    }
    
  5. If no features were found, notify the user with a MessageBox.
    private void FindTask_ExecuteCompleted(object sender, FindEventArgs args)
    {
    	GraphicsLayer graphicsLayer = MyMap.Layers["MyGraphicsLayer"] as GraphicsLayer;
    graphicsLayer.Graphics.Clear();
    
    	 graphicsLayer.Renderer = new SimpleRenderer()
      {
          Symbol = LayoutRoot.Resources["ResultsSymbol"] as ESRI.ArcGIS.Client.Symbols.Symbol 
      };
    
      graphicsLayer.Graphics.Clear();
    
      List<Graphic> graphics = new List<Graphic>();
    
      if (args.FindResults.Count > 0)
      {
          foreach (FindResult result in args.FindResults)
          {
              ESRI.ArcGIS.Client.Graphic graphic = new ESRI.ArcGIS.Client.Graphic()
              {
                  Geometry = result.Feature.Geometry,
              };
              graphics.Add(graphic);                    
          }
          graphicsLayer.Graphics.AddRange(graphics);
      }
    	else
    	{
    		MessageBox.Show("No features found");
    	}
    }
    

Handling execution errors

  1. Declare a handler for the Find task's Failed event. This handler will be invoked if there is a problem with executing a find operation.
    private void FindTask_Failed(object sender, TaskFailedEventArgs args)
    {
    }
    
  2. Notify the user of the problem with a MessageBox.
    private void FindTask_Failed(object sender, TaskFailedEventArgs args)
    {
    	MessageBox.Show("Find failed: " + args.Error);
    }
    

The Find task with online data

The Find task can also be performed on online data. To use the sample with online data modify the URL inputted into the Find task constructor to be a URL to a feature layer in an ArcGIS for Server map service. To find a URL, you can use the ArcGIS Services Directory. The example below uses the states layer of the ESRI Census USA service. Set the layer to be searched by adding a layer ID of 3 to the Layerids property. This corresponds to the counties layer. Specify that the NAME field to be searched in the find operation to match the attribute field of the online service.

private void FindButton_Click(object sender, RoutedEventArgs e)
        {

FindTask findTask = new FindTask("http://sampleserver1.arcgisonline.com/ArcGIS/rest/services/" +
		"Demographics/ESRI_Census_USA/MapServer/");
	findTask.ExecuteCompleted += FindTask_ExecuteCompleted;
	findTask.Failed += FindTask_Failed;

	FindParameters findParameters = new FindParameters();
	findParameters.LayerIds.AddRange(new int[] { 3 });
	findParameters.SearchFields.AddRange(new string[] { "NAME" });
	findParameters.ReturnGeometry = true;

	findParameters.SearchText = FindTextBox.Text;

	findTask.ExecuteAsync(findParameters);

        }

To display an online service in the Map Control the local service will need to be replaced by an online service.

<Window x:Class="ArcGISWpfApplication1.MainWindow"
	xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
 xmlns:esri="http://schemas.esri.com/arcgis/client/2009"
 Title="MainWindow">
	<Grid>

		<!-- MAP -->
		<esri:Map x:Name="MyMap">
	<esri:Map.Layers>
				<esri:ArcGISTiledMapServiceLayer ID="StreetMapLayer" 
					Url="http://server.arcgisonline.com/ArcGIS/rest/services/ESRI_StreetMap_World_2D/MapServer"/>	
			</esri:Map.Layers>
		</esri:Map>
	</Grid>
</Window>

1/27/2015