Creating server solutions using .NETScenarios  

Creating a Map Service Browser


Concepts

A Web service is a set of related application functions that can be programmatically invoked over the Internet. ArcGIS Server Web services can be accessed by any development language that can submit SOAP-based requests to a Web service and process SOAP-based responses. The Web service consumer can get the methods and types exposed by the Web service through its Web Service Description Language (WSDL). ArcGIS Web Services are based on the SOAP doc/literal format and are interoperable across Java and .NET.

Developers can use the Web Service Publisher to publish any server object as a Web service over HTTP. For any published server object, the Web Service Publisher creates an HTTP endpoint (URL) to which SOAP requests can be submitted using standard HTTP POST. The endpoint also supports returning the WSDL for the Web service using a standard HTTP GET with "wsdl" as the query string. A collection of these HTTP endpoints is called a Web service catalog.

The implementation of the HTTP endpoint is thin, while the actual processing of the SOAP request and the generation of the SOAP response take place within the GIS server. The WSDLs that describe the definitions of the SOAP requests and responses are also part of the GIS server and are installed as part of the ArcGIS Server install under <install directory>\XMLSchema.

ArcGIS Web services can be consumed from .NET or Java. As a consumer of an ArcGIS Web service, you can use the methods exposed by the Web service by including a reference to the Web service in your .NET or Java project. Web services implemented on a Java Web server can be consumed from a .NET client and vice versa.

This scenario describes the process of building, deploying, and consuming the ArcGISWS_MapServerBrowser  ArcGIS developer sample.

You can find this sample in: <install location>\DeveloperKit\SamplesNET\Server\Desktop_Client_Applications

Design

This application makes use of the methods and objects defined by the ArcGIS Server Web service catalog and MapServer Web service. These methods and objects correspond to the set of ArcObjects necessary to call the stateless methods exposed by the MapServer object.

To support this application, you need to have access to a Web service catalog that contains at least one MapServer Web service. Note: The Web service catalog itself can be either .NET or Java. For purposes of this example, the assumption is that the Web service is .NET.

The application will connect to a Web service catalog and present to the user a list of the available MapServer Web services in the Web service catalog. The application will list the available data frames in the map and any bookmarks associated with the data frame that the user can pick from. In addition, the user can navigate the map using navigation tools in a toolbar.

Requirements

The requirement for working through this scenario is that you have access to an ArcGIS Server Web service catalog that contains at least one MapServer Web service. The machine on which you develop this application does not need to have any ArcGIS software or licensing installed on it.

For the purposes of this example, assume you have access to a Web service catalog created with .NET with the following URL:

http://localhost/arcgis/services

The Web service catalog contains a MapServer Web service for a MapServer object called "USA_Cached" whose URL is:

http://localhost/arcgis/services/USA_Cached/MapServer

It will be easiest to follow this walkthrough if you create a Web service catalog with the same name as above (MyWebServiceCatalog) and a MapServer server object and Web service (USA_Cached), although this is not necessary. For more information about creating a map service from a map document see Publishing Web services.

If you have your own Web service catalog and MapServer Web services with different names, the points at which the difference in names will impact the code for this application will be pointed out.

The IDE used in this example is Visual Studio .NET 2005. This Web application can be implemented with other .NET IDEs.

Implementation

In this scenario, you will use the Windows Application template project that is installed as part of Visual Studio .NET that you will add your functionality to. The code for this scenario will be written in C#; however, you can also write this application using VB.NET.

The first step is to create the new project.

Creating a new project

  1. Start Visual Studio .NET.
  2. Click File, click New, then click Project.
  3. In the New Project dialog box, under Project Types, click the Visual C# Projects category. Under Templates, click Windows Application.
  4. For the application name, type "MapServerBrowser".
  5. Click OK. This will create a new project that contains a single Windows form.

Adding references to the Web services

For your application to have access to the methods and objects exposed by Web service catalog and MapServer Web services, you need to add Web references to both the Web service catalog and the MapServer Web service to your application. A Web reference enables you to use objects and methods provided by a Web service in your code. After adding a Web reference to your project, you can use any element or functionality provided by that Web service within your application.



  1. In the Solution Explorer, right-click References and click Add Web Reference.


     
  2. For URL, type the URL of your Web service catalog with "wsdl" as the query string. In this example, the URL would be:

    http://localhost/arcgis/services?wsdl
  3. Click Go.
  4. Once the Web service is found, type "WebCatalog" for the Web reference name, then click Add Reference.


  5. In the Solution Explorer, right-click References and click Add Web Reference.
  6. For URL, type the URL of your MapServer Web service with "wsdl" as the query string. In this example, the URL would be:

    http://localhost/arcgis/services/USA_Cached/MapServer?wsdl
     
  7. Click Go.
  8. Once the Web service is found, type MapServerWS for the Web reference name, then click Add Reference.

In the project's class view, expand MapServerBrowser, then expand MapServerWS and WebCatalog to see the classes that have been added to your project by referencing the Web services. Now that these references have been added to the project, you will start programming your Windows application to make use of the classes and methods provided by these Web service references to consume ArcGIS Server Web services.



Setting the properties of the form

To accommodate the functionality for this application, you will add a number of user interface controls to the windows form. Before doing that, you need to set some properties on the form itself, such as its size and text.

  1. In the Solution Explorer, double-click Form1.cs. This will open the form in design mode.
  2. In the Properties for the form, type "584, 596" for the Size property and type "MapServerBrowser" for the Text property.

Adding controls to the form

This application utilizes a number of user controls that you need to add to and arrange on the form.

The first control you'll add is a picture box that will display the map images returned by the MapServer Web service.

  1. In the Microsoft Visual Studio .NET toolbox, click the All Windows Forms tab to display the Windows Forms tools.
  2. In the toolbox, click PictureBox and drag a picture box onto the form.
  3. In the picture box's properties, type "552, 400" for the Size property and type "12, 152" for the Location property.
  4. Click Fixed3D for the BorderStyle property.

The next set of controls you'll add is to handle the user input for the URL of the Web service catalog that the user of the application wants to connect to.
  1. In the Microsoft Visual Studio .NET toolbox, click the Windows Forms tab to display the Windows Forms tools.
  2. In the toolbox, click Label and drag a label onto the form.
  3. In the label's properties, type "Web Service Catalog URL:" for the Text property, "140, 20" for the Size property, and "12, 64" for the Location property.
  4. In the Windows Forms toolbox, click TextBox and drag a text box onto the form.
  5. In the text box's properties, type "txtServer" for the (Name) property, "288, 20" for the Size property, and "156, 64" for the Location property.
  6. In the Windows Forms toolbox, click Button and drag a button onto the form.
  7. In the button's properties, type "btnConnect" for the (Name) property, "Get Web Services" for the Text property, "104, 23" for the Size property, and "452, 64" for the Location property.

The next controls you'll add are the controls to list the MapServer Web services that are in the Web service catalog specified by the user.

  1. In the Microsoft Visual Studio .NET toolbox, click the Windows Forms tab to display the Windows Forms tools.
  2. In the toolbox, click Label and drag a label onto the form.
  3. In the label's properties, type "Map Server Web service:" for the Text property, "128, 20" for the Size property, and "12, 92" for the Location property.
  4. In the Microsoft Visual Studio .NET toolbox, click the Windows Forms tab to display the Windows Forms tools.
  5. In the toolbox, click ComboBox and drag a combo box onto the form.
  6. In the combo box's properties, type "cboMapServer" for the (Name), "400, 21" for the Size property, and "156, 92" for the Location property.
  7. Click DropDownList for the DropDownStyle property.
  8. Click False for the Enabled property.

The next set of controls you'll add is the controls to list the data frames in the MapServer Web service selected by the user.

  1. In the Microsoft Visual Studio .NET toolbox, click the Windows Forms tab to display the Windows Forms tools.
  2. In the toolbox, click Label and drag a label onto the form.
  3. In the label's properties, type "Data frame:" for the Text property, "64, 20" for the Size property, and "12, 120" for the Location property.
  4. In the Microsoft Visual Studio .NET toolbox, click the Windows Forms tab to display the Windows Forms tools.
  5. In the toolbox, click ComboBox and drag a combo box onto the form.
    While this example includes exact control placement and size on the form, you can also arrange these controls interactively by dragging them and sizing them with the mouse.

  6. In the combo box's properties, type "cboDataFrame" for the (Name), "224, 21" for the Size property, and "156, 120" for the Location property.
  7. Click DropDownList for the DropDownStyle property.
  8. Click False for the Enabled property.

The next set of controls you'll add is the toolbar and its buttons for navigating the map.

  1. In the Microsoft Visual Studio .NET toolbox, click the Windows Forms tab to display the Windows Forms controls.
  2. In the toolbox, click ToolStrip and drag a toolstrip onto the form. The toolstrip in Visual Studio.NET 2005 is similar to the toolbar in Visual Studio.NET 2003.  The toolstrip will automatically size and position itself along the top of the form.  Note that since a toolstrip may be non-visual, it is also added to the component tray below the form.
  3. Type "28,28" for the ImageScalingSize property.

Add tools to the toolstrip.

  1. In the toolstrip properties, click the Items property to open the Items Collection Editor.  This dialog will be used to add custom tools to the toolstrip.  
  2. Select Button in the item drop down list and click the Add button.
  3. Select the button item in the Members section of the dialog.  Click the Image property and set it to: <install_location>\DeveloperKit\SamplesNET\Server\data\icons\zoomin.gif.
  4. Type "tbZoomIn" for the Name.
  5. Click False for the Enabled property.
  6. Click 0 for the ImageIndex property.
  7. Type "Zoom in" for the ToolTipText property.
  8. Repeat steps 2 through 7 with the following properties:
    Image:  <install_location>\DeveloperKit\SamplesNET\Server\data\icons\zoomout.gif   Name: tbZoomOut    Enabled: False   ToolTipText: Zoom out 
  9. Repeat steps 2 through 7 with the following properties:
    Image: <install_location>\DeveloperKit\SamplesNET\Server\data\icons\fullext.gif   Name: tbFullExt   Enabled: False   ToolTipText: Full Extent  
  10. Repeat steps 2 through 7 with the following properties:
    Image: <install_location>\DeveloperKit\SamplesNET\Server\data\icons\pan.gif   Name: tbPan Enabled: False   ToolTipText: Pan  CheckOnClick : True   
  11. Click OK to close the Items Collection Editor.



The last controls you'll add are the controls to list the bookmarks in the MapServer Web service's data frame selected by the user.

  1. In the Microsoft Visual Studio .NET toolbox, click the Windows Forms tab to display the Windows Forms tools.
  2. In the toolbox, click Label and drag a label onto the form.
  3. In the label's properties, type "Bookmark:" for the Text property, "60, 16" for the Size property, and "260, 16" for the Location property.
  4. In the Microsoft Visual Studio .NET toolbox, click the Windows Forms tab to display the Windows Forms tools.
  5. In the toolbox, click ComboBox and drag a combo box onto the form.
  6. In the combo box's properties, type "cboBookMark" for the (Name), "228, 21" for the Size property, and "320, 12" for the Location property.
  7. Click DropDownList for the DropDownStyle property.
  8. Click False for the Enabled property.

Now you have added all the controls necessary for the application to the form. Your form should look like the following in design mode:


Now you'll add the code to the events for the controls in the application.

Adding member variables to the application

This application requires a number of private member variables. Each variable will not be explained here, but as you add code to the various control events, you will use these variables.

  1. Right-click the form and click ViewCode. This will open the code window for the form.

    You will see that there are already a number of private member variables that were added by Visual Studio .NET for the controls you added to the form.

  2. Below these member variables, add the following lines of code:
    private string m_sSelectedMap;
    private MapServerWS.MapDescription m_sMapDesc;
    private string m_sDataFrame;
    private double startX;
    private double startY;
    private int startDragX;
    private int startDragY;
    private int deltaDragX;
    private int deltaDragY;
    private MapServerWS.ImageDisplay idisp;
    private System.Drawing.Image pImage;
    private Boolean ispanning;

Adding code to get the list of Web services in the catalog

When using this application, the user will type the URL of a Web service catalog, then click the Get Web Services button. You will add code to the click event of the button to get the MapServer Web services in the Web service catalog and add them to the Web service combo box (cboMapServer).

  1. In the Solution Explorer, double-click Form1.cs to open the form in design mode or click the Form1.cd [Design] tab.
  2. Double-click the Get Web Services button you added to the form. This will open the code window and place the cursor in the default event for the button, which is the click event. The code for the click event should look like the following:
    private void btnConnect_Click(object sender, System.EventArgs e)
    {
              
    }
    

    You will add code to the click event to connect to the Web service catalog and get the list of MapServer Web services to add to the Web service combo box.

    First, since the operation may take a few seconds to execute, you'll want to indicate to the user that the application is busy. To do this, you will set the cursor to a wait cursor. You'll also add code to clear the contents of the Web services combo box.


  3. Add the following lines of code to the click event:
    this.Cursor = Cursors.WaitCursor;
    cboMapServer.Items.Clear();
    
    
    The remainder of the code will run within a try/catch block. If an error occurs in the code in the try block, the code in the catch block will be executed. The catch block will change the cursor back to a normal cursor and display a message box containing an error message.
  4. Add the following lines of code to the click event:
    try
    {
      this.Cursor = Cursors.Default;
    }
    catch (Exception exception)
    {
      this.Cursor = Cursors.Default;
      MessageBox.Show(exception.Message  ,"An error has occurred");
    }
    

    Next, you need to add code to create an instance of a WebCatalog.Default object. WebCatalog is the name of the Web service catalog Web reference you added earlier in this scenario, and Default is the name of the actual Web service. Once you create a WebService.Default, you'll set the URL property to be the URL specified by the use in the txtServer text box.

  5. Add the following lines of code to your try block:
    WebCatalog.Default sc = new WebCatalog.Default();
    sc.Url = txtServer.Text;
    

    Next you'll call the GetServiceDescriptions method on the Web service catalog to get an array of ServiceDescription objects. You'll loop through this array and get the URLs for the MapServer Web services and add their URLs to the Web service combo box (cboMapServer). Finally, you'll add code to enable the cboMapServer combo box.

  6. Add the following lines of code to the try block:
    WebCatalog.ServiceDescription[] wsdesc = sc.GetServiceDescriptions();
    WebCatalog.ServiceDescription sd = null;
    
    for (int i = 0;i < wsdesc.Length;i++)
    {
      sd = wsdesc[i];
      if (sd.Type == "MapServer")
      {
        cboMapServer.Items.Add(sd.Url);
      }          
    }
    cboMapServer.Enabled = true;
    

The completed code for the click event of your button should look like the following:

private void btnConnect_Click(object sender, System.EventArgs e)
{
If you named your Web service catalog Web reference something other than WebCatalog, then you'll have to modify this code to reflect the naming difference.

For example, if you named your Web reference MyWebRef, then the object would be MyWebRef.Default.

  this.Cursor = Cursors.WaitCursor;
  cboMapServer.Items.Clear();

  try
  {
    WebCatalog.Default sc = new WebCatalog.Default();
    sc.Url = txtServer.Text;
    WebCatalog.ServiceDescription[] wsdesc = sc.GetServiceDescriptions();
    WebCatalog.ServiceDescription sd = null;
               
    for (int i = 0;i < wsdesc.Length;i++)
    {
      sd = wsdesc[i];
      if (sd.Type == "MapServer")
      {
        cboMapServer.Items.Add(sd.Url);
      }
    }

    cboMapServer.Enabled = true;
    this.Cursor = Cursors.Default;
  }
  catch (Exception exception)
  {
    this.Cursor = Cursors.Default;
    MessageBox.Show(exception.Message  ,"An error has occurred");
  }
}

Adding code to get the data frames from the MapServer

Once connected to the Web service catalog, the user will pick a MapServer Web service with which to work. You will add code to the selected index changed event of the Web service combo box (cboMapServer) to get the list of data frames in the MapServer and add them to the data frames combo box (cboDataFrame). You'll also set some of the member variables added earlier.

  1. In the Solution Explorer, double-click Form1.cs to open the form in design mode, or click the Form1.cd [Design] tab.
  2. Double-click the MapServer Web service combo box you added to the form. This will open the code window and place the cursor in the default event for the combo box, which is the selected index changed event. The code for the selected index changed event should look like the following:
    private void cboMapServer_SelectedIndexChanged(object sender, System.EventArgs e)
    {
              
    }
    

    One of the member variables you added to the form was a string called m_sSelectedMap. You will use this variable to remember what the URL of the currently selected MapServer Web service is. This will be the value of the Text property of the map server combo box.


  3. Add the following line of code to your event:
    m_sSelectedMap = cboMapServer.Text;
    

    The rest of the code for this event will be in a try/catch block.

  4. Add the following lines of code to the event.
    try
    {
    
    }
    catch(Exception exception)
    {
      MessageBox.Show(exception.Message  ,"An error has occurred");
    }
    

    Next you need to add code to create an instance of a MapServerWS.USA_Cached object. MapServerWS is the name of the MapServer Web service Web reference you added earlier in this scenario, and USA_Cached is the name of the actual Web service. Once you create a MapServerWS.USA_Cached, you'll set the URL property to be the value of the m_sSelectedMap member variable.

  5. Add the following lines of code to your try block:
    MapServerWS.USA_Cached map = new MapServerWS.USA_Cached();
    map.Url = m_sSelectedMap;
    

    m_sDataFrame is another string member variable that you added to the form. This member variable will store the name of the currently selected data frame. By default, when a new MapServer Web service is selected, the default data frame is picked for the user. So, you'll set the value of m_sDataFrame to be the default data frame of the MapServer.

  6. Add the following line of code to your try block:
    m_sDataFrame = map.GetDefaultMapName();
    

    Next, you'll add code to loop through all the data frames in the Web service and add them to the data frame combo box (cboDataFrame). Once the combo box is populated, set its text property to be the value of m_sDataFrame.

  7. Add the following code to your try block:
    // Get dataframes, populate dropdown.
    cboDataFrame.Items.Clear();
    for (int i=0;i<map.GetMapCount();i++)
    {
      cboDataFrame.Items.Add(map.GetMapName(i));
    }
    cboDataFrame.Text = m_sDataFrame;
    
  8. Finally, add the following code to your try block to enable the other controls and toolbar buttons on the form.
    // Enable controls.
    cboDataFrame.Enabled = true;
    cboBookMark.Enabled = true;
    IEnumerator benum = toolStrip1.Items.GetEnumerator();
    
    If the MapServer Web service that you created your Web reference from was not called USA_Cached, then the name of the object will not be MapServerWS.USA_Cached, but will be the name of your MapServer.

    For example, if you referenced a MapServer Web service called "World", then the object's name would be MapServerWS.World.

    When you set the Text property on the cboDataFrame combo box, it will trigger its selected index changed event. You'll add code to that event next.

    ToolStripItem btn = null;
    while (benum.MoveNext())
    {
      btn = benum.Current as ToolBarButton;
      btn.Enabled = true;
    }
    

The completed code for the selected index changed event should look like the following:

private void cboMapServer_SelectedIndexChanged(object sender, System.EventArgs e)
{
  m_sSelectedMap = cboMapServer.Text;
  try
  {
    MapServerWS.USA_Cached map = new MapServerWS.USA_Cached();
    map.Url = m_sSelectedMap;
    
    m_sDataFrame = map.GetDefaultMapName();  

    // Get dataframes, populate dropdown.
    cboDataFrame.Items.Clear();
    for (int i=0;i<map.GetMapCount();i++)
    {
      cboDataFrame.Items.Add(map.GetMapName(i));
    }
    cboDataFrame.Text = m_sDataFrame;
       
    // Enable controls.
    cboDataFrame.Enabled = true;
    cboBookMark.Enabled = true;
    IEnumerator benum = toolStrip1.Items.GetEnumerator();
    ToolStripItem btn = null;
    while (benum.MoveNext())
    {
      btn = benum.Current as ToolBarButton;
      btn.Enabled = true;
    }
  }
  catch(Exception exception)
  {
    MessageBox.Show(exception.Message  ,"An error has occurred");
  }
}

Adding code to get the bookmarks for the selected data frame

You will add code to the selected index changed event of the data frame combo box (cboDataFrame) to get the list of spatial bookmarks for the selected data frame and add them to the bookmarks combo box (cboBookMark). You'll also set some of the member variables added earlier.

  1. In the Solution Explorer, double-click Form1.cs to open the form in design mode, or click the Form1.cd [Design] tab.
  2. Double-click the data frame combo box you added to the form. This will open the code window and place the cursor in the default event for the combo box, which is the selected index changed event. The code for the selected index changed event should look like the following:
    private void cboDataFrame_SelectedIndexChanged(object sender, System.EventArgs e)
    {
              
    }
    

    Since the data frame has changed, you'll set the data frame member variable (m_sDataFrame) to be the Text value of the data frame combo box.

    Add the following line of code to the event:
    m_sDataFrame = cboDataFrame.Text;
    

    The rest of the code for this event will be in a try/catch block.


  3. Add the following lines of code to the event.
    try
    {
    
    }
    catch(Exception exception)
    {
      MessageBox.Show(exception.Message  ,"An error has occurred");
    }
    

    Next, you need to add code to create an instance of a MapServerWS.USA_Cached object and set the URL property to be the value of the m_sSelectedMap member variable.

  4. Add the following lines of code to your try block:
    MapServerWS.USA_Cached map = new MapServerWS.USA_Cached();
    map.Url = m_sSelectedMap;
    

    To get a list of the spatial bookmarks in the map, you'll add code to get a reference to the MapServer's MapServerWS.MapServerInfo object for the selected data frame, then get an array of bookmarks from MapServerInfo.

    Once you have the array of bookmarks, you'll loop through the array and add the names of the bookmarks to the bookmarks combo box (cboBookMark). Notice the code also adds a "<Default Extent>" item to the combo box. This allows the user to return to the default extent of the data frame.


    If the MapServer Web service that you created your Web reference from was not called USA_Cached, then the name of the object will not be MapServerWS.USA_Cached but will be the name of your MapServer.

    For example, if you referenced a MapServer Web service called "World", then the object's name would be MapServerWS.World.

  5. Add the following lines of code to your try block:
    // Get bookmarks, populate bookmarks dropdown.
    MapServerWS.MapServerInfo mapi = map.GetServerInfo(m_sDataFrame);
    MapServerWS.MapServerBookmark[] pMSBookMarks = mapi.Bookmarks;
      
    cboBookMark.Items.Clear();
    cboBookMark.Items.Add("<Default Extent>");
    MapServerWS.MapServerBookmark pMDBook;
    for (int j = 0;j<pMSBookMarks.Length;j++)
    {
      pMDBook = pMSBookMarks[j];
      cboBookMark.Items.Add(pMDBook.Name);
    }
    cboBookMark.SelectedItem = "<Default Extent>";
    

The completed code for the selected index changed event should look like the following:

private void cboDataFrame_SelectedIndexChanged(object sender, System.EventArgs e)
{
  m_sDataFrame = cboDataFrame.Text;

  try
  {
    // Find the info about the selected data frame from the map server.
    MapServerWS.USA_Cached map = new MapServerWS.USA_Cached();
    map.Url = m_sSelectedMap;
  
    // Get bookmarks, populate bookmarks dropdown.
    MapServerWS.MapServerInfo mapi = map.GetServerInfo(m_sDataFrame);
    MapServerWS.MapServerBookmark[] pMSBookMarks = mapi.Bookmarks;
  
    cboBookMark.Items.Clear();
    cboBookMark.Items.Add("<Default Extent>");
    MapServerWS.MapServerBookmark pMDBook;
    for (int j = 0;j<pMSBookMarks.Length;j++)
    {
      pMDBook = pMSBookMarks[j];
      cboBookMark.Items.Add(pMDBook.Name);
    }
    cboBookMark.SelectedItem = "<Default Extent>";
  }
  catch(Exception exception)
  {
    MessageBox.Show(exception.Message  ,"An error has occurred");
  }
}
When you set the Text property on the cboBookMark combo box, it will trigger its selected index changed event. You'll add code to that event later.

Adding helper function to draw the map

To this point, the code you have added to the application has used methods on the Web service catalog and MapServer Web services to get information about a particular Web service catalog or MapServer Web service and populate controls on the form.

The next set of controls whose events you'll add code to will actually draw the map and display the result in the picture box. To draw the map, you'll add a helper function that these events will call to do the map drawing.

  1. Add the following function to your form:
    private void drawMap(ref MapServerWS.MapDescription pMapDescription)
    {
    
    }
    

    As you can see, the drawMap function takes a single argument of type MapServerWS.MapDescription. The map description object is used by a MapServer when drawing maps to allow, at draw time, various aspects of the map to be changed, without changing the running MapServer object. These properties include the extent to draw, the layers to turn on or off, and so on. In this respect, the map description serves the purpose of allowing stateless use of a MapServer while allowing these aspects of state to be saved in the application by saving and modifying a local copy of the map description.

    This function doesn't do anything to the map description except use it to draw the map. You'll see later how the code that calls the drawMap function modifies and uses the map description to allow the user to navigate around the map.

    The first code you'll add to this function creates an instance of a MapServerWS.USA_Cached object and sets the URL property to be the value of the m_sSelectedMap member variable.

  2. Add the following lines of code to the function block:
    MapServerWS.USA_Cached map = new MapServerWS.USA_Cached();
    map.Url = m_sSelectedMap;
    
  3. Next, add the following lines of code to create an image description object that you'll use when drawing the map. Notice that the ImageDisplay object (idisp) is one of the member variables you added to the form. You will see later that this ImageDisplay object will be used to query the map in the events for the Pan command.
    // Set up the image description for the output.
    MapServerWS.ImageType it = new MapServerWS.ImageType();
    it.ImageFormat  = MapServerWS.esriImageFormat.esriImageJPG;
    it.ImageReturnType = MapServerWS.esriImageReturnType.esriImageReturnMimeData;
                      
    idisp = new MapServerWS.ImageDisplay();
    idisp.ImageHeight = 400;
    idisp.ImageWidth  = 552;
    idisp.ImageDPI  = 150;
    MapServerWS.ImageDescription pID = new MapServerWS.ImageDescription();
    pID.ImageDisplay = idisp;
    pID.ImageType = it;
    
    By definition, Web services are stateless. For more information about programming ArcGIS Server and how it relates to managing state in an application that makes stateless use of server objects, see the Managing Application State section.

    The size of the
    ImageDisplay is the same as the size of the picture box control. This will produce an image that fits exactly in the picture box on the form.
  4. Finally, add the following lines of code to call the ExportMapImage method on the MapServer to draw the map, then convert the result into a .NET Image object and set it as the Image property of the picture box, then return. Notice that the Image object (pImage) is a member variable of the form. This will be used again later in the events for the Pan button.

    MapServerWS.MapImage pMI = map.ExportMapImage(pMapDescription, pID);
    System.IO.Stream pStream = new System.IO.MemoryStream((byte[])pMI.ImageData);
    pImage = Image.FromStream(pStream);
    
    pictureBox1.Image = pImage;
    pictureBox1.Refresh();
    
    return;
    

The completed code for the drawMap function should look like the following:

private void drawMap(ref MapServerWS.MapDescription pMapDescription)
{
  MapServerWS.USA_Cached map = new MapServerWS.USA_Cached();
  map.Url = m_sSelectedMap;
  
  // Set up the image description for the output.  
  MapServerWS.ImageType it = new MapServerWS.ImageType();
  it.ImageFormat  = MapServerWS.esriImageFormat.esriImageJPG;
  it.ImageReturnType = MapServerWS.esriImageReturnType.esriImageReturnMimeData;
                  
  idisp = new MapServerWS.ImageDisplay();
  idisp.ImageHeight = 400;
  idisp.ImageWidth  = 552;
  idisp.ImageDPI  = 150;
        
  MapServerWS.ImageDescription pID = new MapServerWS.ImageDescription();
  pID.ImageDisplay = idisp;
  pID.ImageType = it;
      
  MapServerWS.MapImage pMI = map.ExportMapImage(pMapDescription, pID);
  System.IO.Stream pStream = new System.IO.MemoryStream((byte[])pMI.ImageData);
  pImage = Image.FromStream(pStream);

  pictureBox1.Image = pImage;
  pictureBox1.Refresh();

  return;
}

Adding code to draw the extent of the selected bookmark

When the user clicks the bookmarks combo box and picks a bookmark, the picture box will display the map for the extent of the selected bookmark. To do this you'll add code to the selected index changed event of the bookmarks combo box (cboBookMark).

  1. In the Solution Explorer, double-click Form1.cs to open the form in design mode, or click the Form1.cd [Design] tab.
  2. Double-click the bookmarks combo box you added to the form. This will open the code window and place the cursor in the default event for the combo box which is the selected index changed event. The code for the selected index changed event should look like the following:
    private void cboBookMark_SelectedIndexChanged(object sender, System.EventArgs e)
    {
              
    }
    

    Since the operation may take a few seconds to execute, you'll want to indicate to the user that the application is busy. To do this, you will set the cursor to a wait cursor.

  3. Add the following line of code to the click event:
    this.Cursor = Cursors.WaitCursor;
    

    The rest of the code for this event will be in a try/catch block.

  4. Add the following lines of code to the event:
    try
    {
    
    }
    catch(Exception exception)
    {
      this.Cursor = Cursors.Default;
      MessageBox.Show(exception.Message  ,"An error has occurred");
    }
    

    Next you need to add code to create an instance of a MapServerWS.USA_Cached object and set the URL property to be the value of the m_sSelectedMap member variable. Then you'll get a MapServerInfo object for the selected data frame (m_sDataFrame).

  5. Add the following lines of code to your try block:
    MapServerWS.USA_Cached map = new MapServerWS.USA_Cached();
    map.Url = m_sSelectedMap;
    MapServerWS.MapServerInfo mapi = map.GetServerInfo(m_sDataFrame);
    
    
    Double-click the bookmarks combo box to open the code window in the combo box's selected index changed event.
  6. Next, add code to create a MapServerWS.MapDescription variable. If the Text value of the bookmarks combo box is "<Default Extent>", then set the map description to be the default map description for the data frame, update the map description member variable m_sMapDesc, draw the map, and return. Otherwise, set the map description to be the map description member variable.
    MapServerWS.MapDescription pMapDescription;
        
    // If they chose the default extent, get the map description from the map // server, then exit.
    if(cboBookMark.Text == "<Default Extent>")
    {
      pMapDescription = mapi.DefaultMapDescription;
      m_sMapDesc = pMapDescription;
      drawMap (ref pMapDescription);
      this.Cursor = Cursors.Default;
      return;
    }
    
    pMapDescription = m_sMapDesc;
    

    A copy of the map description is being stored in a member variable, because as you implement other commands, such as the zoom and pan commands, you'll want to keep track of the user's current extent to know what to zoom or pan from. These commands will modify the extent of the local copy of the map description, then use it as input to the drawMap function.

  7. Next, add the following code to find the bookmark that corresponds to the bookmark picked by the user, get its extent, set the extent into the map description, then draw the map. Notice that after the map description is modified, the member variable m_sMapDesc is updated so the user's current extent is remembered.
    // Find the chosen bookmark.
    MapServerWS.MapServerBookmark[] pMSBookMarks = mapi.Bookmarks;
    MapServerWS.MapServerBookmark pMDBook = null;
                   
    for (int i = 0;i < pMSBookMarks.Length;i++)
    {
        pMDBook = pMSBookMarks[i];
        if (pMDBook.Name == cboBookMark.Text)
          break;
    }
    
    // Set the extent of the map description to the bookmark's extent.
    MapServerWS.MapArea pMA = pMDBook;
    pMapDescription.MapArea = pMA;
      
    // Save the map description.
    m_sMapDesc = pMapDescription;
    drawMap (ref pMapDescription);
    
    this.Cursor = Cursors.Default;
    

The completed code for the selected index changed event should look like the following:

private void cboBookMark_SelectedIndexChanged(object sender, System.EventArgs e)
{
  this.Cursor = Cursors.WaitCursor;

  try
  {
    MapServerWS.USA_Cached map = new MapServerWS.USA_Cached();
    map.Url = m_sSelectedMap;

    MapServerWS.MapServerInfo mapi = map.GetServerInfo(m_sDataFrame);
    MapServerWS.MapDescription pMapDescription;
    
    // If they chose the default extent, get the map description from the          
// map server, then exit. if(cboBookMark.Text == "<Default Extent>") { pMapDescription = mapi.DefaultMapDescription; m_sMapDesc = pMapDescription; drawMap (ref pMapDescription); this.Cursor = Cursors.Default; return; } pMapDescription = m_sMapDesc; // Find the chosen bookmark. MapServerWS.MapServerBookmark[] pMSBookMarks = mapi.Bookmarks; MapServerWS.MapServerBookmark pMDBook = null; for (int i = 0;i < pMSBookMarks.Length;i++) { pMDBook = pMSBookMarks[i]; if (pMDBook.Name == cboBookMark.Text) break; } // Set the extent of the map description to the bookmark's extent.
    MapServerWS.MapArea pMA = pMDBook;
    pMapDescription.MapArea = pMA;
  
    // Save the map description.
    m_sMapDesc = pMapDescription;
    drawMap (ref pMapDescription);

    this.Cursor = Cursors.Default;
  }
  catch (Exception exception)
  {
    this.Cursor = Cursors.Default;
    MessageBox.Show(exception.Message  ,"An error has occurred");
  }
}

Adding code for the Zoom, Full Extent buttons

Some of the map navigation functionality in this application includes fixed zoom in, fixed zoom out, and zoom to full extent commands. You added these commands as buttons to the toolstrip on the form. You'll add the code to execute when these commands are clicked to the button-click event of the toolstrip (toolStrip1).

  1. In the Solution Explorer, double-click Form1.cs to open the form in design mode, or click the Form1.cd [Design] tab.
  2. Double-click the toolstrip you added to the form. This will open the code window and place the cursor in the default event for the toolstrip, which is the button-click event. The code for the button-click event should look like the following:
    private void toolStrip1_ItemClicked(object sender, ToolStripItemClickedEventArgs e) 
    {

    }
  3. Add the following code to determine which button was clicked.
    // Evaluate the Button property to determine which button was clicked.
    switch
    (toolStrip1.Items.IndexOf(e.ClickedItem)){
      case 0: // Zoom in.
        break;
      case 1: // Zoom out.
        break;
      case 2: // Full extent.
        break;
      case 3: // Pan.
        break;
    }
    

    For the zoom in case, you'll add code to get the MapServerWS.USA_Cachedobject, set its URL, get the map description saved as m_sMapDesc, shrink its extent, draw the map, then update m_sMapDesc with the new extent.

  4. Add the following code to case 0.
     this.Cursor =
    Cursors.WaitCursor;
    try
      { MapServerWS.USA_Cached map = new
      MapServerWS.USA_Cached(); map.Url =
              
      m_sSelectedMap; MapServerWS.MapDescription pMapDescription =
           
      m_sMapDesc; // Get the current extent and shrink it, then set the new extent                                                                                                        into the map description.
      MapServerWS.EnvelopeN pEnvelope = pMapDescription.MapArea.Extent as MapServerWS.EnvelopeN;
    
      double eWidth = Math.Abs(pEnvelope.XMax - pEnvelope.XMin);
      double eHeight = Math.Abs(pEnvelope.YMax  - pEnvelope.YMin);
      double xFactor = (eWidth - (eWidth * 0.75))/2;
      double yFactor = (eHeight - (eHeight * 0.75))/2;
      pEnvelope.XMax = pEnvelope.XMax - xFactor;
      pEnvelope.XMin = pEnvelope.XMin + xFactor;
      pEnvelope.YMax = pEnvelope.YMax - yFactor;
      pEnvelope.YMin = pEnvelope.YMin + yFactor;
    
      MapServerWS.MapExtent pMapExtext = new MapServerWS.MapExtent();
      pMapExtext.Extent = pEnvelope;
      pMapDescription.MapArea = pMapExtext;
           
      // Save the map description and draw the map.
      m_sMapDesc = pMapDescription;
      drawMap (ref pMapDescription);
              
      this.Cursor = Cursors.Default;  
    }
    catch (Exception exception)
    {
      this.Cursor = Cursors.Default;
      MessageBox.Show(exception.Message  ,"An error has occurred");
    }
    

    For the zoom out case, you'll add similar code as that for zoom in, except you'll expand the map description's extent.

  5. Add the following code to case 1.
    this.Cursor = Cursors.WaitCursor;
     try
    {
      MapServerWS.USA_Cached map = new MapServerWS.USA_Cached();
      map.Url = m_sSelectedMap;
           
      MapServerWS.MapDescription pMapDescription = m_sMapDesc;
    
     // Get the current extent and shrink it, then set the new extent             into the map description.
      MapServerWS.EnvelopeN pEnvelope = pMapDescription.MapArea.Extent as MapServerWS.EnvelopeN;
    
      double eWidth = Math.Abs(pEnvelope.XMax - pEnvelope.XMin);
      double eHeight = Math.Abs(pEnvelope.YMax  - pEnvelope.YMin);
      double xFactor = ((eWidth * 1.25) - eWidth)/2;
      double yFactor = ((eHeight * 1.25) - eHeight)/2;
      pEnvelope.XMax = pEnvelope.XMax + xFactor;
      pEnvelope.XMin = pEnvelope.XMin - xFactor;
      pEnvelope.YMax = pEnvelope.YMax + yFactor;
      pEnvelope.YMin = pEnvelope.YMin - yFactor;
    
      MapServerWS.MapExtent pMapExtext = new MapServerWS.MapExtent();
      pMapExtext.Extent = pEnvelope;
      pMapDescription.MapArea = pMapExtext;
           
      // Save the map description and draw the map.
      m_sMapDesc = pMapDescription;
      drawMap (ref pMapDescription);
    
      this.Cursor = Cursors.Default;
    }
    catch (Exception exception)
    {
      this.Cursor = Cursors.Default;
      MessageBox.Show(exception.Message  ,"An error has occurred");
    }
    

    For the full extent case, the code is similar, except you get the full extent from the map server's MapServerInfo object and set it into the map description.

  6. Add the following code to case 2.
    this.Cursor = Cursors.WaitCursor;
    try
    {
      MapServerWS.USA_Cached map = new MapServerWS.USA_Cached();
      map.Url = m_sSelectedMap;
      MapServerWS.MapServerInfo mapi = map.GetServerInfo(m_sDataFrame);
      
      MapServerWS.MapDescription pMapDescription = m_sMapDesc;
      
      // Get the full extent of the map and set it as the map description's extent.
      MapServerWS.Envelope pEnvelope = mapi.FullExtent;
      
      MapServerWS.MapExtent pMapExtext = new MapServerWS.MapExtent();
      pMapExtext.Extent = pEnvelope;
      pMapDescription.MapArea = pMapExtext;
    
     
      // Save the map description and draw the map.
      m_sMapDesc = pMapDescription;
      drawMap(ref pMapDescription);
                   
      this.Cursor = Cursors.Default;
    }
    catch (Exception exception)
    {
      this.Cursor = Cursors.Default;
      MessageBox.Show(exception.Message  ,"An error has occurred");
    }
    

The code for the button-click event should look like the following:

private void toolStrip1_ItemClicked(object sender, ToolStripItemClickedEventArgs e) 
{

// Evaluate the Button property to determine which button was clicked.
switch (toolStrip1.Items.IndexOf(e.ClickedItem))
{ case 0: this.Cursor = Cursors.WaitCursor; // Zoom in. try { MapServerWS.USA_Cached map = new MapServerWS.USA_Cached(); map.Url = m_sSelectedMap; MapServerWS.MapDescription pMapDescription = m_sMapDesc; // Get the current extent and shrink it, then set the new extent // into the map description. MapServerWS.EnvelopeN pEnvelope = pMapDescription.MapArea.Extent as MapServerWS.EnvelopeN; double eWidth = Math.Abs(pEnvelope.XMax - pEnvelope.XMin); double eHeight = Math.Abs(pEnvelope.YMax - pEnvelope.YMin); double xFactor = (eWidth - (eWidth * 0.75))/2; double yFactor = (eHeight - (eHeight * 0.75))/2; pEnvelope.XMax = pEnvelope.XMax - xFactor; pEnvelope.XMin = pEnvelope.XMin + xFactor; pEnvelope.YMax = pEnvelope.YMax - yFactor; pEnvelope.YMin = pEnvelope.YMin + yFactor; MapServerWS.MapExtent pMapExtext = new MapServerWS.MapExtent(); pMapExtext.Extent = pEnvelope; pMapDescription.MapArea = pMapExtext; // Save the map description and draw the map. m_sMapDesc = pMapDescription; drawMap (ref pMapDescription);
          
          this.Cursor = Cursors.Default;  
        }
        catch (Exception exception)
        {
          this.Cursor = Cursors.Default;
          MessageBox.Show(exception.Message  ,"An error has occurred");
        }

        break; 
    case 1: // Zoom out.     
                         
      this.Cursor = Cursors.WaitCursor;

      try
      {
        MapServerWS.USA_Cached map = new MapServerWS.USA_Cached();
        map.Url = m_sSelectedMap;
       
        MapServerWS.MapDescription pMapDescription = m_sMapDesc;
       
        // Get the current extent and shrink it, then set the new extent              // into the map description.
        MapServerWS.EnvelopeN pEnvelope = pMapDescription.MapArea.Extent as MapServerWS.EnvelopeN;

        double eWidth = Math.Abs(pEnvelope.XMax - pEnvelope.XMin);
        double eHeight = Math.Abs(pEnvelope.YMax  - pEnvelope.YMin);
        double xFactor = ((eWidth * 1.25) - eWidth)/2;
        double yFactor = ((eHeight * 1.25) - eHeight)/2;
        pEnvelope.XMax = pEnvelope.XMax + xFactor;
        pEnvelope.XMin = pEnvelope.XMin - xFactor;
        pEnvelope.YMax = pEnvelope.YMax + yFactor;
        pEnvelope.YMin = pEnvelope.YMin - yFactor;

        MapServerWS.MapExtent pMapExtext = new MapServerWS.MapExtent();
        pMapExtext.Extent = pEnvelope;
        pMapDescription.MapArea = pMapExtext;
       
        // Save the map description and draw the map.
        m_sMapDesc = pMapDescription;
        drawMap (ref pMapDescription);

        this.Cursor = Cursors.Default;
      }
      catch (Exception exception)
      {
        this.Cursor = Cursors.Default;
        MessageBox.Show(exception.Message  ,"An error has occurred");
      }
      break; 
    case 2: // Full extent
                         
      this.Cursor = Cursors.WaitCursor;
      try
      {
        MapServerWS.USA_Cached map = new MapServerWS.USA_Cached();
        map.Url = m_sSelectedMap;
        MapServerWS.MapServerInfo mapi = map.GetServerInfo(m_sDataFrame);
  
        MapServerWS.MapDescription pMapDescription = m_sMapDesc;
  
        // Get the full extent of the map and set it as the map                   
// description's extent. MapServerWS.Envelope pEnvelope = mapi.FullExtent; MapServerWS.MapExtent pMapExtext = new MapServerWS.MapExtent(); pMapExtext.Extent = pEnvelope; pMapDescription.MapArea = pMapExtext; // Save the map description and draw the map. m_sMapDesc = pMapDescription; drawMap(ref pMapDescription); this.Cursor = Cursors.Default; } catch (Exception exception) { this.Cursor = Cursors.Default; MessageBox.Show(exception.Message ,"An error has occurred"); } break; case 3: //Pan break; } }

You will implement case 3 (pan) next.

Adding code for the Pan button

The final piece of functionality you'll add to the application is the ability for the user to interactively pan the map. To do this, you'll add code to a number of events on the picture box control, specifically, the mouse down, mouse up, mouse move, and paint events. Before adding code to those events, you'll add code to the button-click event of the toolstrip to change the cursor for the picture box when the Pan button is pushed in (Checked).  When the pan button is pressed, the code within case 3 is executed.  The Checked property of a ToolStripButton represents the current state of the button.  If the pan button is clicked on when it is not checked (not active), Checked will return false in the button-click event for the toolstrip.  The Checked property will be set to True after the toolstrip events are finished.  The ispanning member variable is used by the application to determine if the application will pan when a mouse event occurs on the map (pciture box control).  

Add the following lines of code to case 3 in the toolbar-click event.

ispanning = false; 
ToolStripButton btn = (ToolStripButton)e.ClickedItem;
if (!btn.Checked) {
ispanning = true;
pictureBox1.Cursor = Cursors.Hand;
} else {
pictureBox1.Cursor = Cursors.Default;
}
Next, you will add code to the mouse down event for the picture box. This code will check to see if the Pan button is pushed and, if so, record the point on the map (for example, picture box) that is clicked in both screen and map coordinates, then save those coordinates in two of the member variables you added to the form.
  1. In the Solution Explorer, double-click Form1.cs to open the form in design mode, or click the Form1.cd [Design] tab.
  2. Click the picture box control on the form.
  3. In the picture box's properties, click the Event button, then double-click the MouseDown event. This will open the code window and place the cursor in the mouse down event. The code for the mouse event should look like the following:
    private void pictureBox1_MouseDown(object sender, System.Windows.Forms.MouseEventArgs e)
    {
    
    }
    
  4. Add the following code to the event to verify if the left mouse button is clicked and, if not, return.
    if(e.Button != MouseButtons.Left)
        return;
    

    Next, verify the Pan button is pushed and, if it is, create an instance of the MapServerWS.USA_Cached object, and use the map description and image display variables (m_sMapDesc and idisp) with the ToMapPoints method on the MapServer to convert the screen coordinates of the point clicked to map coordinates. Then save the map and screen coordinates as the member variables startX, startY, startdragX, and startdragY.

  5. Add the following code to the event:
    // Is pan enabled?
    IEnumerator benum = toolStrip1.Items.GetEnumerator();
    ToolStripItem btn = null;
    while (benum.MoveNext())
    {
      btn = benum.Current as ToolBarButton;
      if (toolStrip1.Items.IndexOf(btn) == 3 && ispanning)
      {
        MapServerWS.USA_Cached map = new MapServerWS.USA_Cached();
        map.Url = m_sSelectedMap;
           
        MapServerWS.MapDescription pMapDescription = m_sMapDesc;
        int[] Xs = {e.X};
        int[] Ys = {e.Y};
        MapServerWS.MultipointN mpnt = map.ToMapPoints(m_sMapDesc,idisp,Xs,Ys) as MapServerWS.MultipointN;
        MapServerWS.Point[] pnta = mpnt.PointArray;
        MapServerWS.PointN pnt = pnta[0] as MapServerWS.PointN;
        startX = pnt.X;
    startY = pnt.Y; startDragX = e.X; startDragY = e.Y; } }


    The completed code for the mouse down event should look like the following:

    private void pictureBox1_MouseDown(object sender, System.Windows.Forms.MouseEventArgs e)
    {
      if(e.Button != MouseButtons.Left)
        return;
    
      // Is pan enabled?
      IEnumerator benum = toolStrip1.Items.GetEnumerator();
      ToolStripItem btn = null;
      while (benum.MoveNext())
      {
        btn = benum.Current as ToolBarButton;
        if (toolStrip1.Items.IndexOf(btn) == 3 && ispanning)
        {
          MapServerWS.USA_Cached map = new MapServerWS.USA_Cached();
          map.Url = m_sSelectedMap;
           
          MapServerWS.MapDescription pMapDescription = m_sMapDesc;
          int[] Xs = {e.X};
          int[] Ys = {e.Y};
          MapServerWS.MultipointN mpnt = map.ToMapPoints(m_sMapDesc,idisp,Xs,Ys) as MapServerWS.MultipointN;
          MapServerWS.Point[] pnta = mpnt.PointArray;
          MapServerWS.PointN pnt = pnta[0] as MapServerWS.PointN;
          startX = pnt.X;
          startY = pnt.Y;
          startDragX = e.X;
          startDragY = e.Y;
        }
      }               
     }
    

    The next event you'll implement is the mouse move. In the mouse move event, you'll add code similar to the code you added in the mouse down event to verify the Pan button is pushed and the mouse button is the left mouse button. The code that executes when these conditions are met calculates the amount the mouse has moved from the original point that was clicked on the picture box (startX, startY) and stores the difference as member variables deltaDragX and deltaDragY. It then forces a redraw of the picture box by calling its Invalidate method.

  6. In the Solution Explorer, double-click Form1.cs to open the form in design mode, or click the Form1.cd [Design] tab.
  7. Click the picture box control on the form.
  8. In the picture box's properties, click the Event button, then double-click the MouseMove event. This will open the code window and place the cursor in the mouse move event. The code for the mouse move event should look like the following:
    private void pictureBox1_MouseMove(object sender, System.Windows.Forms.MouseEventArgs e)
    {
    
    }
    
  9. Add code to your mouse move event, so it looks like the following:
    private void pictureBox1_MouseMove(object sender, System.Windows.Forms.MouseEventArgs e)
    {
      if (e.Button != MouseButtons.Left)
        return;
    
      IEnumerator benum = toolStrip1.Items.GetEnumerator();
      ToolStripItem btn = null;
      while (benum.MoveNext())
      {
        btn = benum.Current as ToolBarButton;
        if (toolStrip1.Items.IndexOf(btn) == 3 && ispanning)
        {
          // Drag the image.
          pictureBox1.Image = null;
          deltaDragX = startDragX - e.X;
          deltaDragY = startDragY - e.Y;
          pictureBox1.Invalidate();
        }
      }
    }
    

    The Invalidate method triggers the picture box's Paint event. Next you'll add code to the paint event to draw the image offset from the picture box as the mouse is dragged.

  10. In the Solution Explorer, double-click Form1.cs to open the form in design mode, or click the Form1.cd [Design] tab.
  11. Click the picture box control on the form.
  12. In the picture box's properties, click the Event button, then double-click the Paint event. This will open the code window and place the cursor in the paint event. The code for the paint event should look like the following:
    private void pictureBox1_MouseMove(object sender, System.Windows.Forms.MouseEventArgs e)
    {
    
    }
    

    The code you add to this event gets the image from the member variable pImage, then applies the offset of the upper left corner of the image based on the values of deltaDragX and deltaDragY and creates a new rectangle the same width as the

    picture, but offset, and draws the image in that offset rectangle. The effect is that as the user drags the mouse, it will appear as though the map is being dragged. This feedback makes it easier for the user to effectively pan the map.
  13. Add code to your paint event, such that it looks like the following:
    private void pictureBox1_Paint(object sender, System.Windows.Forms.PaintEventArgs e)
    {
      // Get the image.
      Image newImage = pImage;
       
      if (newImage != null)
      {
        // Create rectangle for displaying image.
        Point loc = pictureBox1.Location;
        Rectangle destRect = new Rectangle(pictureBox1.Left - loc.X - deltaDragX, pictureBox1.Top - loc.Y - deltaDragY, pictureBox1.Width, pictureBox1.Height);
        // Draw image to screen.
        e.Graphics.DrawImage(newImage, destRect);
      }
    }
    

    The last event to implement is the mouse up event. The code in this event will execute when the user has completed the pan and releases the button.

  14. In the Solution Explorer, double-click Form1.cs to open the form in design mode, or click the Form1.cd [Design] tab.
  15. Click the picture box control on the form.
  16. In the picture box's properties, click the Event button, then double-click the MouseUp event. This will open the code window and place the cursor in the mouse up event. The code for the mouse up should look like the following:
    private void pictureBox1_MouseUp(object sender, System.Windows.Forms.MouseEventArgs e)
    {
    
    }
    

    Like the mouse down and mouse move events, your code will check to verify the left mouse button is clicked and that the Pan button is pushed. Once that is verified, your code will get the MapServerWS.USA_Cachedobject, get the user's current extent from the map description (m_sMapDesc), apply the deltaX and deltaY offset to its coordinates, update the map description (m_sMapDesc), and redraw the map. It also resets the deltaDragX and deltaDragY member variables to 0 for the next pan operation.

  17. Add code to your mouse up event, so it looks like the following:
    private void pictureBox1_MouseUp(object sender, System.Windows.Forms.MouseEventArgs e)
    {
      if(e.Button != MouseButtons.Left)
        return;
    
      // Is pan enabled?
      IEnumerator benum = toolStrip1.Items.GetEnumerator();
      ToolStripItem btn = null;
      while (benum.MoveNext())
      {
        btn = benum.Current as ToolBarButton;
        if (toolStrip1.Items.IndexOf(btn) == 3 && ispanning)
        {
          this.Cursor = Cursors.WaitCursor;
          MapServerWS.USA_Cached map = new MapServerWS.USA_Cached();
          map.Url = m_sSelectedMap;
           
          MapServerWS.MapDescription pMapDescription = m_sMapDesc;
          int[] Xs = {e.X};
          int[] Ys = {e.Y};
          MapServerWS.MultipointN mpnt = map.ToMapPoints(m_sMapDesc,idisp,Xs,Ys) as MapServerWS.MultipointN;
          MapServerWS.Point[] pnta = mpnt.PointArray;
          MapServerWS.PointN pnt = pnta[0] as MapServerWS.PointN;
    
          double deltaX = pnt.X - startX;
          double deltaY = pnt.Y - startY;
    
          // Change the extent and draw.
          MapServerWS.EnvelopeN pEnvelope = pMapDescription.MapArea.Extent as MapServerWS.EnvelopeN;
    
          pEnvelope.XMax = pEnvelope.XMax - deltaX;
          pEnvelope.XMin = pEnvelope.XMin - deltaX;
          pEnvelope.YMax = pEnvelope.YMax - deltaY;
          pEnvelope.YMin = pEnvelope.YMin - deltaY;
    
          MapServerWS.MapExtent pMapExtext = new MapServerWS.MapExtent();
          pMapExtext.Extent = pEnvelope;
          pMapDescription.MapArea = pMapExtext;
           
          // Save the map description and draw the map.
          m_sMapDesc = pMapDescription;
          drawMap (ref pMapDescription);
    
          deltaDragX = 0;
          deltaDragY = 0;
    
          pictureBox1.Invalidate();
          this.Cursor = Cursors.Default;
        }
      }
    }
    

Your application is now ready to be tested. Compile the project (Build/Build Solution) and fix any errors.

Testing the application

If you run the application from within Visual Studio (Debug/Start), it will open the form.

  1. Type the URL of a Web service catalog.
  2. Choose one of the MapServer Web services.
  3. Click on the Bookmark combo box and choose a bookmark to zoom to.
  4. Use the Zoom In, Zoom Out, Full Extent, and Pan buttons to navigate around the map.
  5. Try choosing different MapServer Web services and data frames.



    You can use this application to connect to any ArcGIS Server Web service catalog, whether that Web service catalog was written in .NET or Java.



Deployment

Because this application requires no ArcGIS software or licensing to run, you can simply build an executable and copy it to any machine that has the .NET framework installed. If a user has access and knows the URL to an ArcGIS Server Web service catalog, the application's functionality can be utilized.

Additional Resources

This scenario includes functionality and programming techniques for consuming ArcGIS Server Web services over the Internet.  If you are unfamiliar with developing applications that make use of Web services with .NET, it's also recommended that you refer to your .NET developer documentation to become more familiar with .NET application development. If you want to learn more about Web services in general, visit www.w3.org.