ArcGIS SDK  

Development Environments

Walkthrough 1 C#: Creating a simple command for ArcMap

In this walkthrough you will create a class in .NET that implements the esriSystemUI ICommand  interface. You will create the command from first principles, without using any of the ESRI utility classes or add-ins for Visual Studio. To see how to create a similar command in less time by using utility classes and add-ins, see Walkthrough 2.

This command will zoom in to the current map.

Create a new class library

1.   Open Visual Studio .NET.

2.   Click File, then New, and select Project.

3.   In the New Project dialog box, select Visual C# Projects in the left-hand pane.

<CSNewProject.bmp>

4.   Select Class Library in the right-hand pane.

5.   Name the project WalkthroughCS, and browse to the location you wish to save the project.

6.   Now click OK, and the new project will be created for you.

Note Visual Studio will, by default, create a sub-directory of the directory you selected to store your project files. The name of the new directory will be the Project name you specified.

Reference the ESRI object libraries

You will need to add a reference to the ESRI object libraries to your C# project.

In VB 6 or VC++ projects, you add a reference to the ESRI object libraries, which contain information about COM objects. Although .NET cannot use COM objects directly, the COM interoperability services provided by the .NET framework allow you to use COM objects in your .NET project.

Using the tools available in Visual Studio .NET, you can create a runtime callable wrapper which allows a .NET component to create and call methods on a COM object. You can also create a COM callable wrapper, which allows a COM client to use .NET components as if they were COM objects. 

ESRI provides .NET runtime callable wrappers, which will be used to instantiate and make calls on the objects in the ESRI object libraries from your C# project.

Note This walkthrough makes use of the ESRI signed assemblies. We cannot guarantee and do not provide technical support for the functionality of any assemblies you build yourself or that you may have received from third parties.

1.   Click the Project menu, and then click Add Reference. When the Add Reference dialog box opens, make sure to select the .NET tab.

2.   In the upper components list, select the component named ESRI.ArcGIS.ArcMapUI and then click Select.

3.   Repeat step 2 to select the ESRI.ArcGIS.Carto, ESRI.ArcGIS.Framework, ESRI.ArcGIS.Geometry, ESRI.ArcGIS.SystemUI, and System.Drawing assemblies.

     Note The System.Drawing reference adds a reference to a standard Microsoft assembly which contains functionality you'll use later on to create the bitmap on the button face of your command.  The other assemblies are ESRI assemblies to help you work with the ESRI object libraries.

<CSAddReference.bmp>

4.   Click OK to dismiss the dialog box.

Create a zoom-in command

1.   In the Solution Explorer, right-click the project, click Add, and then click Add New Item.

2.   In the Add New Item dialog box, select Class in the right-hand pane, and then in the Textbox below, name the Class ZoomIn.cs. Click Open.

<CSNewZIClass.bmp>

3.   In the Solution Explorer, right-click the existing class (Class1.cs), and then click Delete. 

<CSDelClass1.bmp>

Add a using directive

In VC++ and VB6, to reference an object correctly, you specify a fully qualified name, the item name preceded by the library name, for example, esriSystemUI.ICommand .

In .NET, you fully qualify a variable by using it's Namespace. Namespaces are a concept in .NET that allow objects to be organized hierarchically, regardless of the assembly they are defined in. For most ESRI classes, the namespace and assembly name are the same. For example, the wrapper classes in the ESRI.ArcGIS.SystemUI assembly all belong to the namespace ESRI.ArcGIS.SystemUI.

To make your code simpler and more readable, you will add directives that act as shortcuts to items in some of the namespaces in your project.

1.   In the Solution Explorer, double-click ZoomIn.cs to open its code window.

2.   At the top of the code window, add the lines of code shown below.

  using ESRI.ArcGIS.ArcMapUI;
  using ESRI.ArcGIS.Carto;
  using ESRI.ArcGIS.Framework;
  using ESRI.ArcGIS.Geometry;
  using ESRI.ArcGIS.SystemUI;
  using System.Runtime.InteropServices;

Tip Remember that C# is case-sensitive. If you start by typing "ESRI.", the auto-completion feature of IntelliSense will allow you to complete the next section of code by pressing Tab.

Note You will use the System.Runtime.InteropServices namespace later on in this walkthrough.

Implement the ICommand  interface

The C# environment provides a feature to allow you to quickly implement an existing interface.

1.   In the ZoomIn.cs code window, specify that the ZoomIn class implements the ICommand  interface as shown below.

  public class ZoomIn : ICommand

2.   Display the Class View window. To open the Class View window, either press CTRL+SHIFT+C or choose Class View on the View menu.

3.   Navigate to the Bases and Interfaces of the ZoomIn class, and select the ICommand  interface.

4.   Right-click the ICommand  interface, click Add, and then click Implement Interface.

<CSImplICmd.bmp>

Note Visual Studio stubs out the members of ICommand  in the code window automatically, bracketing the stubs within a region named "Implementation of ICommand".

5.   Press Crtl+Shift+S to save all the files in your project.

Adding code to the members of ICommand

You will now start adding C# .NET code to the members of ICommand .

1.   Add the following member variables to the class as shown. The members of the ICommand  interface will use them.

  public class ZoomIn : ICommand
  {
    private IApplication m_app;
    private bool m_enabled;
    private System.Drawing.Bitmap m_bitmap;
    private IntPtr m_hBitmap;
    ...

Note By convention member variables are declared at the beginning of the class definition, within the curly brackets, although the declarations can be placed anywhere within a class. Member variables of a class are often referred to as class fields  in .NET.

2.   In the ZoomIn class code window, scroll down to find the OnCreate  method, and add the code shown below.

  public void OnCreate(object hook)
  {
    if (hook != null)
    {
      if (hook is IMxApplication)
      {
        m_app = (IApplication) hook;
        m_enabled = true;
      }
    }
  }

3.   Just above OnCreate , you should see the OnClick method; add the following code to perform the zoom action on the current ActiveView .

  public void OnClick()
  {
    IMxDocument mxDoc = (IMxDocument) m_app.Document;
    IActiveView activeView = mxDoc.ActiveView;
    
    IEnvelope currExtent = (IEnvelope) activeView.Extent;
    currExtent.Expand(0.5D, 0.5D, true);
    activeView.Extent = currExtent;
    activeView.Refresh();
  }

4.   Edit the code stubs as shown to complete the Category , Caption , Checked , Enabled , Name , Message , and Tooltip  methods to return information about the command.

  public string Caption
  {
    get
    {
       return "Zoom In x 0.5 C#";
    }
  }
  public string Category
  {
    get
    {
       return "Developer Samples";
    }
  }
  public bool Checked
  {
    get
    {
       return false;
    }
  }
  public bool Enabled
  {
    get
    {
      return m_enabled;
    }
  }
  public string Message
  {
    get
    {
      return "Zooms the display to half the current extent.";
    }
  }
  public string Name
  {
    get
    {
      return "Developer Samples_Zoom In C#";
    }
  }
  public string Tooltip
  {
    get
    {
      return "Zoom In x 0.5 C#";
    }
  }

Note As you are not implementing a help system for the command, you can leave the default implementation of the HelpContextID  and HelpFile  properties.

Returning the ICommand::Bitmap  property

You will now add an existing bitmap file to your project, and use this file to return the icon used for the Zoom In command's button face.

1.   In the Solution Explorer window, right-click the WalkthroughCS project, click Add, then click Add Existing Item.

<CSAddExisting.bmp>

2.   In the Add Existing Item dialog, click the Files of Type pull down and select Image Files.

<CSFilesOfType.bmp>

3.   Browse to the \Bin\Icons directory of your ArcGIS install, and select Zoom_in_tool_2.bmp, and then click OK.

Note The Zoom_in_tool_2.bmp file is copied to your project directory by default.

4.   In the Solution Explorer window, ensure the new bitmap is selected, and in the Properties window below change the Build Action property to Embedded Resource.

<CSEmbedRes.bmp>

5.   Return to the code window for the command, and add the following code to the class constructor.

  public ZoomIn()
  {
    string[] res = GetType().Assembly.GetManifestResourceNames();
    if (res.GetLength(0) > 0)
    {
      m_bitmap = new System.Drawing.Bitmap(GetType().Assembly.GetManifestResourceStream(res[0]));
      if (m_bitmap != null)
      {
        m_bitmap.MakeTransparent(m_bitmap.GetPixel(0,0));
        m_hBitmap = m_bitmap.GetHbitmap();
      }
    }
  }

Note The GetHbitmap  method creates an unmanaged  Windows GDI bitmap object. This object must be deleted specifically, as the .NET runtime garbage collectors cannot clear up unmanaged resources. You will delete the bitmap object in steps 6 and 7.

Tip The class constructor is a method that is called when the class is created. You can use it to set up members of the class. The constructor method has the same name as the class, and is differs from other methods, in that it has no return type.

6.   Scroll up the code window and add the following code inside the scope of the class declaration, as shown below.

  public class ZoomIn : ICommand
  {
    [DllImport("gdi32.dll")]
    static extern bool DeleteObject(IntPtr hObject);
    ...

Note The DllImport statement above provides access to the DeleteObject  function in the Windows GDI library, Gdi32.dll.

7. Scroll back down the code window, and add the following function just below the class constructor. This destructor function will clear up the Windows GDI bitmap object that was created by the GetHBitmap  call in the constructor.

  ~ZoomIn()
  {
    if (m_hBitmap.ToInt32() != 0)
      DeleteObject(m_hBitmap);
  }

Tip This code is will be called when the garbage collector needs to destroy the instance of the ZoomIn  class.

8.   Finally, add the following code to the ICommand::Bitmap method, to return the handle of the bitmap.

  public int Bitmap
  {
    get
    {
      return m_hBitmap.ToInt32();
    }
  }

Expose the ZoomIn  class to COM

ArcGIS expects your command class to be a COM class; therefore, you must specify that the .NET class you have created is also exposed as a COM class by creating a COM callable wrapper for it.

1.   In the Solution Explorer window, right-click the WalkthroughCS project, and click Properties.

2.   In the Project Properties dialog box, select Configuration Properties; then, click Build. In the right-hand pane change the Register For COM Interop property to True. Click OK.

<CSREgComInterop.bmp>

Note  Setting the Register For COM Interop property to true will invoke the Assembly Registration Tool (Regasm.exe). This will add the information about your class to the registry that a COM client would expect to find.

3.   Return to the code window, and add the following code at the beginning of the ZoomIn  class declaration, to specify attributes required by COM, completing the GUID attribute with a new GUID value.

  [ClassInterface(ClassInterfaceType.None)]
  [Guid("ENTER000-GUID-HERE-0000-000000000000")]
  public class ZoomIn : ICommand
    {
       ...

Note You can generate a new GUID by using the GuidGen.exe utility included with Visual Studio .NET (found on the Tools menu of Visual Studio .NET). The GUID should be specified in the format shown above, without curly braces.

Tip As shown in the above code, a semicolon does not follow attribute declarations.

Adding COM category registration functions

Previously, you may have used the ESRI Component Category Manager or the ESRI Compile and Register addin for Visual Basic 6 to add a class to a component category. VC++ and ATL users may have added component category information using registry scripts.

Using Visual Studio .NET you can specify a function that executes when an assembly exposed for COM interop is registered on a system, and another function that executes when the assembly is unregistered. This allows you the to automatically register your class to a component category when the assembly is registered.

1.    Scroll the code window to near the top of the class declaration, and add the following code to the class just below the member variables, defining functions that register and unregister the ZoomIn  class to the ESRI Mx Commands component category.

  #region Component Category Registration
  
  [ComRegisterFunction()]
  [ComVisible(false)]
  static void RegisterFunction(String sKey)
  {
    string fullKey = sKey.Remove(0, 18) + @"\Implemented Categories";
    Microsoft.Win32.RegistryKey regKey = Microsoft.Win32.Registry.ClassesRoot.OpenSubKey(fullKey, true);
    if (regKey != null)
    {
      regKey.CreateSubKey("{B56A7C42-83D4-11D2-A2E9-080009B6F22B}");
    }
  }
  
  [ComUnregisterFunction()]
  [ComVisible(false)]
  static void UnregisterFunction(String sKey)
  {
    string fullKey = sKey.Remove(0, 18) + @"\Implemented Categories";
    Microsoft.Win32.RegistryKey regKey = Microsoft.Win32.Registry.ClassesRoot.OpenSubKey(fullKey, true);
    if (regKey != null)
    {
      regKey.DeleteSubKey("{B56A7C42-83D4-11D2-A2E9-080009B6F22B}");
    }
  }
  
  #endregion#

Tip This code also defines a collapsible region called Component Category Registration. You can define regions like this to help you structure your code. Click the + or - sign shown to the left of the code to collapse or expand the region.

Note The ComVisible attribute is set to false to ensure that this method cannot be called directly by a COM client. It does not affect the method being called when the assembly is registered with COM.

2.   Press Crtl+Shift+S to save all the files in your project.

Compiling the project

Now you are ready to build your project.

1.   Click Build, and then click Build Solution.

Tip The shortcut key to build your solution is Ctrl+Shift+B.

2.   Look at the Output window at the bottom of the Visual Studio .NET IDE. If your project built correctly, you should find a report stating Build succeeded, as shown below.

<CSBuildSuc.bmp>

3.   If your build operation did not succeed, select the Task List window to see what errors are present, and correct the errors as indicated.

The error shown below indicates a simple syntax error, as all lines of C# code need to be terminated by a semicolon.

<CSBuildErr.bmp>

Tip If you double-click the task, the line of code causing the error will automatically be selected for you.

4.   You can check the results of the Build operation by looking in the sub-directories of your project. By default, you will build a Debug version of your project, and the DLL that results from the Build operation will be stored in the \Bin\Debug directory. This directory should also contain debug information (.pdb) and a type library (.tlb) file, produced by the Assembly Registration tool.

Note The Obj sub-directory of the project directory contains temporary files used by the compiler and by Visual Studio.

Use the command in ArcMap

You are now ready to use the ZoomIn command in ArcMap.

1.   Start ArcMap, and open a map document which has some data layers.

2.   Right-click a toolbar to open the Customize dialog box, and select the Developer Samples category.

<CSCustDlg.bmp>

 

3.   In the right-hand pane, select the Zoom In x 0.5 command, and drag it on to an ArcMap toolbar. Close the Customize dialog box.

4.   Click the new command button to zoom in to the map.

5.   Save the document, and then close ArcMap.

Stepping through the command with the debugger

If you wish, you can run the command in debug mode, and step through each line of code in turn.

1.   Return to the Visual Studio .NET environment.

2.   In the Solution Explorer window, right-click the WalkthroughCS project, and click Properties.

3.   In the Project Properties dialog box, select Configuration Properties, then click Debugging.

4.   In the right-hand pane, under the Start Action heading, select the Debug Mode property and change its value to Program, then click the Apply button.

5.  Now select the Start Application property. Click the browse button on the right, and in the dialog box that appears, browse to ArcMap.exe, and click OK.

<CSDebugProg.bmp>

6.   Close the Project Properties dialog box.

7.   In the code window, find the class constructor method, and then click next to it in the Margin Indicator bar, the gray area on the left side of the Code Editor, to set a breakpoint.

<CSBreakPoint.bmp>

8.   Click Debug, then click Start. Visual Studio will run ArcMap , and as soon as the ZoomIn class is instantiated, the debugger will switch back to Visual Studio .NET and allow you to step through the code one line at a time, using the buttons on the Debugging toolbar.

Once you have completed the walkthroughs, you may wish to try deploying your customizations to another computer. See the Deploying topic for more information.