Clippable index grid example

Description The project
provides an index grid for a Map that can be clipped to a certain shape. An accompanying factory coclass allows the map grid
to be created by using the standard ArcMap user interface. The
property pages allow the properties of the grid to be set via the ArcMap
user interface.

Design ClippableMapGrid is a
subtype of the MapGrid abstract class and IndexGrid coclass.
ClippableIndexGridFactory is a subtype of the MapGridFactory abstract class.
ClippableGridPage and NewClippableGridPage both implement standard property page
interfaces. A helper coclass, EnumElement, implements IEnumElement.

Open the CustomMapGrid.dsp workspace and build
the project. This will register the CustomMapGrid.dll and register
coclasses to the required component categories.

Open ArcMap and add a few layers to the map in the
default data view.

Zoom the data view to the extent you want to
display.

Create a graphic element defining the shape of the grid you require;
ensure that graphic element is selected before continuing.
If you want to define the shape of the
grid based on a feature in the map, first use the Select Features tool to
select the feature. Then use the Pointer tool, right-click the graphic, and
click Convert Features to Graphics.

Choose the page layout view, right-click the map
frame, and click Properties from the context menu. The Data Frame
Properties dialog box should now be displayed.

Click the New Clippable Index Grid tab and check
the Create new clippable index grid check box. Set the name, columns, rows,
and tab style as required.

Click the Use Selected Data Graphic button to set the selected
graphic element as the shape of the clippable index grid. Click OK to
dismiss the Data Frame Properties dialog box.
You should now be able to see your clippable index grid
displayed around your dataframe.

Notes The grid will draw inside the dataframe, instead of around the edge
of the dataframe, as is more usual for other grids. You may want to
restrict the extent of the data frame further, once the grid is displayed. You
may also want to remove from the map any features that intersect or
fall outside of the grid by applying a definition query to filter the
visible features.

If you used an element for the clip geometry, you may want to return
to the data view and delete the graphic after the grid has been set
up. Alternatively, hide the graphic by setting its color to 'No Color'.

The case for a custom map grid

Maps are often presented in a rectangular formatfor example, the pages
of a road atlas.

However, geographical featurescities, administrative
regions, counties, countries, and riversare most often irregularly
shaped and do not always fit well into a rectangular frame.

When producing maps in ArcGIS, if the area you are
mapping does not conform to a rectangular frame, you can
clip your dataframe to a shape of your choosing, as shown
in the map on the left.

Imagine now that you need to produce an
overview map, dividing this map into sections that indicate
the boundaries of a series of more detailed maps, like
the index map that is often given at the start of a road
atlas. To satisfy this requirement, you can create an overview map with
an overlaid grid using the Grids and Graticules wizard in ArcMap.

Generally, in this situation you would apply an index grid
(called a reference grid in the Grids and Graticules wizard) to your map, which
is specifically designed for this requirement. Displayed on a PageLayout, it divides a map frame into a chosen number
of columns and rows, labelling each division along the axes,
allowing each section to be identified clearly.

Your overview map has an irregular shapebut the
index grid uses a rectangular shape. You can see (left) that
your overview map will have some empty divisions, which
you do not require and which may be misleading.

Alternatively, you could apply a measured grid or a
graticule, dividing the map into sections based on a
chosen map distance or by latitude and longitude. Both of
these grids also use a rectangular grid, and neither is
specifically designed for use as an index map.

By programming with ArcObjects, you have a fourth
optionyou could use a CustomOverlayGrid. This option is not available through the Grids and Graticules wizard.
By using this coclass, you can create a grid based on the
line features of your own data source.

The CustomOverlayGrid, however, labels the grid
lines themselves, not the grid squares created. It is also a
somewhat rigid solution because to change the number
of columns or rows in the grid, you are required to edit
the line features on which your grid is based.

As your requirements for an index map grid are not
met by the standard map grids available in ArcGIS, you
must create a custom map grid.

Creating the clippable index grid

To solve the requirements of this example, you will create a subtype of IndexGrid,
called ClippableIndexGrid. You will implement IMapGrid and IIndexGrid as well as the
standard interfaces for cloning and persistence. To add the custom functionality, you
will also create and implement a custom interface, IClippableIndexGrid.

As the design is based closely on the standard index
grid, you can delegate many of its members to the members of a contained
IndexGrid. You will adapt the standard functionality of this index grid to create a grid that can
follow the shape of the map data or map
framethe most flexible approach being to allow the
grid to be clipped to any chosen shape.

This can be achieved by implementing your own Draw
method, instead of delegating the call to the contained IndexGrid.

To allow users to add a ClippableIndexGrid to a dataframe, you will continue the example by creating a factory object, which can be
used by ArcMap to create instances of your custom grid. You also need to allow the properties of a ClippableIndexGrid to be set and edited
by a user. Both these issues are dealt with in later sections.

Now that the design of the class is decided, you need to
look in more detail at how to implement the important members of each interface
on the ClippableIndexGrid coclass.

Implementing IMapGrid

When your coclass implements IMapGrid, it becomes a map grid
and can be treated as such in the ArcGIS system.

As the ClippableIndexGrid class design uses containment,
most of the members of IMapGrid can be delegated directly to the contained
IndexGrid coclass.

The members of IMapGrid for which you need to modify
behaviorthose which cannot be directly delegated to the IndexGridare discussed in turn below. For the benefit of those adapting this
sample, typical actions that should be performed in each of the members
are also summarized separately in the following table.

IMapGrid members and descriptions

Border

Return or set an IMapGridBorder reference, storing the map grid border.

Draw

You will perform much of the work of IMapGrid in this method. Draw the map grid for a map frame to the given Display. Draw all the components of the map grid: the grid lines, ticks, subticks, tick marks, border, and labels.

ExteriorWidth

Return the width (in display units) of the portion of the grid that is outside the frame.

GenerateGraphics

Generate graphic elements corresponding to the grid lines and store them in the specified graphics container. Your code will be similar to the Draw method, except that instead of drawing geometries to the display with their respective symbols, the symbols and the geometries are put into an element and the element is added to a group element.

LabelFormat

Return or set an IGridLabel reference storing the label format for the map grid labels.

LineSymbol

Return or set an ISymbol reference. Use this to draw the grid lines. If this property is null, you do not need to draw any grid lines.

Name

Return or set a string value indicating the name of the current map grid.

PrepareForOutput

Perform any actions required to prepare the map grid for output to a device. Generally, you would get the Map associated with the MapFrame parameter. From the Map's IActiveView interface, you would get the ScreenDisplay; from the ScreenDisplay, get the DisplayTransformation. Apply the Map's FullExtent as the transformation's Bounds, and the Map's VisibleExtent as the transformation's VisibleBounds. You would also apply the passed in PixelBounds as the DisplayTransformation's DeviceFrame.

QueryLabelVisibility

Return values indicating the visibility of the labels along all four sides of the map grid.

QuerySubTickVisibility

Return values indicating the visibility of the subticks along all four sides of the map grid.

QueryTickVisibility

Return values indicating the visibility of the ticks along all four sides of the map grid.

SetDefaults

Reset all the member variables storing properties of the map grid to their default values.

SetLabelVisibility

Set values indicating the visibility of the labels along all four sides of the map grid.

SetSubTickVisibility

Set values indicating the visibility of the subticks along all four sides of the map grid.

SetTickVisibility

Set values indicating the visibility of the ticks along all four sides of the map grid.

SubTickCount

Return or set an integer indicating the number of subticks to draw between the major ticks.

SubTickLength

Return or set a double indicating the length of the subticks in points.

SubTickLineSymbol

Return or set an ILineSymbol reference storing the LineSymbol used to draw the subtick lines.

TickLength

Return or set a double indicating the length of the major ticks in points.

TickLineSymbol

Return or set an ILineSymbol reference storing the LineSymbol used to draw the major ticks.

TickMarkSymbol

Return or set an IMarkerSymbol reference storing the MarkerSymbol used to draw tick marks at the grid interval intersections. If null, do not draw any tick mark intersections.

Visible

Return or set a Boolean value indicating if the map grid is visible.

The Draw and GenerateGraphics methods

The Draw method is called when a PageLayout containing a
clippable index grid is refreshed. In this method, you must draw all of the
elements of the clippable index grid to the specified Display object.

The GenerateGraphics method is called if the user clicks the Convert
to Graphics button on the Grids property page of the Data Frame
Properties dialog box. In this method, you need to convert your grid
into individual graphic elements.

As the ClippableIndexGrid has a fundamentally different
appearance than the standard IndexGrid and does not display all the items that an
IndexGrid would, you must implement these two methods from scratch
instead of delegating them.

Much of the internal logic required for these two methods
is similar; therefore, you can modularize your code by using a single internal
method to do most of the work for both the Draw and GenerateGraphics methods. In
this example, the internal method DisplayGrid can either draw directly to a
Display or add elements to a GroupElement, depending on the type of parameters it receives.

The creation of the actual appearance of a map grid is done by the
Draw and GenerateGraphics methods.
The Draw method draws the grid to a Display, and the
GenerateGraphics method creates a graphic element
for each part of the grid.
The ClippableIndexGrid uses a general function,
DisplayGrid, to perform either of these acts.

This design helps you keep all your grid calculation and
drawing code in one place, making your code more modular and easier to
update should you need to change how your grid draws.

The following steps describe the main actions of the
DisplayGrid function, illustrated by brief extracts of code; the full code can be found
in the accompanying VC++ example project.

For clarity, the code is described as for the Draw
method. In the accompanying VC++ project you can see how this function deals
with both drawing to a Display and adding graphic elements to a
GroupElement.

The 'clip geometry'

Note that in this section you will use a 'clip
geometry'this is the geometry set by the user on which the shape of the
clippable index grid is based. Its value will come from the ClipGeometry
property of the IClippableIndexGrid interface, which you will implement later.

DisplayGrid Part 1preparing the shape of the clipped grid

The first step to displaying a grid is to calculate the shape of the grid and
the intervals of the grid lines. To do this, you will need to transform from Map space to
PageLayout space.

Get the properties used for drawing from the contained
IndexGrid.

QI the MapFrame for its
IElement interface and get its Geometry.

Store the extent of the grid in the variable ipExtent.
This extent must be in page units, as it will be used later for drawing the
clipped grid to the PageLayout. The source of this
extent depends on whether or not the clip geometry has been set.

a) If the clip geometry is
not specified, the extent is taken from the MapFrame's Geometry from step 2. This is already in page units.

[VC++]

ipFrameGeometry->get_Envelope(&ipExtent);

b) If a clip geometry is
specified, the extent is taken from this Geometrythis geometry is in map
units (ipExtentMap)
and must, therefore, be transformed to page units. This transformation has
a number of steps and is explored in depth below.

You can transform measurements from Map to PageLayout
space by accessing the DisplayTransformation of the MapFrame and Display
passed to Draw and GenerateGraphics.
You can access the appropriate Display object by using the GetScreenDisplay
property of the IActiveView interface of the IGraphicsContainer parameter.

Now that you have the extent Envelopes of the clip
geometry in both map units and page units, you can create an
AffineTransformation2D.

The ipClipGeometryPage variable will hold this clip geometry in
page units for later use in the DisplayGrid method.

Next, calculate the details of the individual grid
cells. From the extent in page units (ipExtent)
calculated in step 3, calculate the grid origin and intervalsfor example, the
minimum, maximum, and intervals are calculated for the x-axis below.

Once you have calculated the shape and extent of the grid, you
can work out the extent of each grid cell.

Your clipped index grid must only draw the cells of the grid
that overlap with the clip geometry.

Create a GeometryBag. You will use this to collect geometries
representing the individual cells in the clipped index grid.

[VC++]

IGeometryCollectionPtr ipGeomCol(CLSID_GeometryBag);

Next, using the clip geometry and the grid cell
information calculated in step 4, create a Polygon representing each cell in
the map grid. Use the IRelationalOperator::Disjoint method to figure out which grid cells
have a non-null intersection with the clip geometry.

If testPt is contained by the clip geometry, then the
label is known to be at the bottom of the grid. If testPt is not contained
by the clip geometry, you need to place the label above a horizontal Segment
or to
the right of a vertical Segment.

Due to the irregular shape of the ClippableIndexGrid, you
will need to work out the positioning of each grid label.

At this point you should also check the value of the
label visibility properties (these are retrieved at the start of the
DisplayGrid
function). For example, as shown in the code below, only draw the
top axis labels if the property indicates they should be visible.

[VC++]

if (bContains == VARIANT_FALSE && labelTopVis)
{
...

Check whether labels should be visible before drawing
them by using the IMapGrid::QueryLabelVisibility method.

Calculate the correct label text based on the
current row and column of the grid, then draw the label. Pass in to the Draw
method of the GridLabel the leftmost point of the Segment (this will be the
FromPoint of the Segment if it is above the grid, and the ToPoint if it is
below the grid), and also the appropriate esriGridAxisEnum constant to
indicate the label's relative position to the Point.

Calculate a map grid label's text from the current grid row and column numbers.

The example code shows one way of solving the problems of drawing a complex
grid. There are, of course, a variety of different approaches to each of the
logic issues encountered.

The Draw method

The Draw method is called by ArcMap when a page layout is
refreshed.

For the ClippableIndexGrid, Draw simply calls the
DisplayGrid function, passing in the references to IDisplay and IMapFrame it
receives. In the discussion of the DisplayGrid function above, it is assumed
that DisplayGrid is called from the Draw method.

The GenerateGraphics
method

The GenerateGraphics method may be called to create a
GroupElement representing the grid.

GenerateGraphics also calls DisplayGrid, but first
creates a GroupElement to pass in; this signifies to DisplayGrid that it should
create and add graphic elements to the GroupElement, rather than drawing the
shapes directly to the Display.

In the GenerateGraphics method, you need to create a GroupElement
containing other graphic elements representing the individual elements of a map grid.

The GenerateGraphics method receives a
GraphicsContainer parameter; use this to get the current Display.

The Display will not be drawing at this point (as this
method is called from the Convert To Graphics button, as discussed),
therefore, you must call StartDrawing on this display to prepare the
device for drawing.

If your code calls StartDrawing, you must ensure you call
FinishDrawing when you have finished drawing to a Display.

You can find the full details of how DisplayGrid creates
graphic elements for the GenerateGraphics method in the accompanying example.

Implementing IIndexGrid

As ClippableIndexGrid is a type of IndexGrid, the
IIndexGrid interface is implemented and most members are delegated directly to
the contained IndexGrid coclass. IIndexGrid inherits from IMapGrid, which has
been previously discussed.

If you are adapting this sample to create a different
type of custom map grid, consider implementing IIndexGrid if your grid will
divide the dataframe into equal sections and if part of your adaptation
involves the specific members of IIndexGrid.

For example, you may want to perform spatial operations
on the extent of standard grid cells, as demonstrated in this example using the
QueryCellExtent method. You may also want to provide access for clients to set
each column and row label themselves, via the XLabel and YLabel properties.

Below is a table describing the typical actions you
should perform for each member of IndexGrid; this table contains only members
that are not inherited from IMapGrid.

IIndexGrid members and descriptions

ColumnCount

Return or set the number of columns in the index grid.

QueryCellExtent

Return the cell extent in page space for the given row and column.

RowCount

Return or set the number of rows in the index grid.

XLabel

Allow read-write access to an array of strings, which you should use as the labels for the columns of the index grid.

YLabel

Allow read-write access to an array of strings, which you should use as the labels for the rows of the index grid.

Creating and Implementing IClippableIndexGrid

Your ClippableIndexGrid needs two more things: you must be able
to uniquely identify this class from other grids when programming, and you
must also provide a way to specify the clip geometry for the grid.

You can achieve both these goals by creating and
implementing the IClippableIndexGrid interface.

The basic shape of the clippable index grid will be set via a new
interface, IClippableIndexGrid.

The read-write IndexGrid property exposes the IndexGrid
contained by your ClippableIndexGrid for convenienceits IClone interface can be
used externally for operations such as cloning and checking for equality. In
normal operation, the contained IndexGrid referenced by this member is set at
the start of the ClippableIndexGrid's constructor.

The read-write ClipGeometry property simply holds the
shape of the gridthe key to the ClippableIndexGrid's shape.

This geometry, which is set in map units, is used as the base for
the grid; only the cells of this base grid that overlap the geometry are
included as part of the final grid.

The value of the ClipGeometry property will be set in two
circumstances: when a ClippableIndexGrid is created by the NewClippableIndexGrid
property page and when the ClipGeometry is reset by the ClippableIndexGrid
property page. You will construct both these property pages in the 'Plugging
your custom grid into ArcMap' section below.

The ClipGeometry property is the key to the functionality of the
ClippableIndexGrid. This property will be set via the user interface using the
property pages you will create later.

Implementing IGraphicsComposite

The presence of IGraphicsComposite indicates that a class is a
composite of other graphic elements. It also allows clients to access those
elements.

The IMapGrid::GenerateGraphics method also returns the
grid in graphic element form. However, IGraphicsComposite is a generic interface, implemented by many ArcObjects coclasses, allowing clients to work
at this generic level.

As the client should not be able to change the composite
parts of the grid, IGraphicsComposite only needs to return a copy of the
elements that compose the grid. This is achieved by the Graphics property,
which returns an element enumeratora class that implements IEnumElement.
Neither of the standard classes that implement IEnumElement can be used in this case, as you cannot add elements to these classes.
Therefore, you will need to create your own enumerator classsee the
'Creating an element enumerator' section below.

IGraphicsComposite is a generic interface, which returns an
enumeration of graphic elements. None of the existing element enumerators are suitable
for this job, so you will create a new enumerator coclass.

As the IGraphicsComposite interface is designed to be
generic, the second parameter passed to the Graphics property is an IUnknown
reference, pData; the expected coclass of this parameter will vary according to
the implementing class. A map grid class would expect pData to be a reference to
the Map with which the grid is associated.

The Graphics method also receives an IDisplay reference,
pDisplay, indicating the Display for which the graphics should be created. Call
the StartDrawing method of the Display to prepare it for drawing.

You can make use of the DisplayGrid function again to
create the actual graphic elements. Create a GroupElement, then call the
DisplayGrid function, passing in a reference to this GroupElement. This
signifies to DisplayGrid that it should create and add graphic elements to the
GroupElement instead of drawing to the display.

Use the DisplayGrid function to fill a
GroupElement with graphic elements representing the map grid.
Then add each individual element from the GroupElement
to an EnumElement to return the Graphics property element enumeration.

At ArcGIS 9, ArcMap does not call
IGraphicsComposite::GetGraphics, but you should implement it to ensure correct operation of your
map grid with future or alternative clients.

Creating an element enumerator

As you cannot add specific elements to the existing
element enumerators, ElementSelection and SimpleElementSelection, you should
create a new enumerator class, named EnumElements, to return an
enumeration from the IGraphicsComposite::Graphics method.

Also, create an interface called IEnumElementAdmin with a
single method called Add that takes an IElement parameter. Implementing this
interface on EnumElement will allow you to add elements to your
element enumerator.

Creating the EnumElement class and
IEnumElementAdmin interface help you to implement
IGraphicsComposite.

To store the elements in the enumerator, declare a member
variable as an Array; add another member variable to store the current array
position of the enumerator.

[VC++]

IArrayPtr m_pElements;
long m_lPosition;

In the IEnumElementAdmin::Add method, add a reference to
pElement to the last position of the array.

Finish the EnumElement class by implementing
IEnumElement, as shown in the accompanying source code. More information on creating
enumerators can be found in Chapter 2, 'Developing Objects'.

Implementing IClone, IPersist, and IPersistStream

Cloning and persistence are essential functions for plugging any
map grid into the ArcGIS system. For example, each time a map grid's
property sheet is displayed, the map grid will be cloned. Persistence is
essential to allow your grid to be saved to and loaded from a map document.

The ClippableIndexGrid example provides a standard
implementation of the IClone, IPersist, and IPersistStream interfaces.

A map grid must implement the standard cloning
and persistence interfaces.

In the implementation of IPersist, the clip geometry,
m_ipClipGeometry, and contained IndexGrid, m_ipIndexGrid, are persisted to the
stream's ObjectStream. The vector arrays of label strings, m_xLabels and
m_yLabels, are persisted as individual strings by first saving the
number of string elements.

See Chapter 2, 'Developing Objects', for more information on cloning
and persistence.

Implementing other kinds of custom grids

If you are designing a different kind of map grid, you
may also want to implement the IProjectedGrid, IMeasuredGrid, or
ICustomOverlayGrid interfaces depending on the design of the grid.

IMeasuredGrid

Consider implementing this interface if your grid is designed to follow
a coordinate system. Measured grids have an origin, and grid lines
are drawn at fixed distance intervals.

IMeasuredGrid members and descriptions

FixedOrigin

Return or set a value indicating if the grid should take its origin from the XOrigin and YOrigin properties (true) or if it is computed dynamically from the data frame (false).

Units

Return or set a constant indicating the units for the intervals and origin.

XIntervalSize

Return or set the interval between grid lines along the x axis.

XOrigin

Return or set the origin of the grid on the x axis.

YIntervalSize

Return or set the interval between grid lines along the y axis.

YOrigin

Return or set the origin of the grid on the y axis.

IProjectedGrid

Consider implementing the IProjectedGrid interface if you
will be exposing a spatial reference for your grid. This interface has a single
member, SpatialReference, indicating the coordinate system of the grid. This
member should be coded to allow an ISpatialReference object to be read or written by reference.

ICustomOverlayGrid

You may want to implement this interface if your grid
will be based on the Features of an existing FeatureClass, and your grid label
text is stored as attributes of those Features.

ICustomOverlayGrid members and descriptions

IDataSource

Return or set an IFeatureClass reference, indicating the data source of the grid lines.

LabelField

Return or set a string indicating the name of the Field in the data source that should be used to label the map grid.

Plugging your custom grid into ArcMap

Now that you have created your custom map grid coclass,
the next step is to enable a user to create a new ClippableIndexGrid within ArcMap and edit the grid's properties. In ArcMap, a user may create a new
instance of an existing grid in one of three ways.

First, a user may create a grid by opening the Data Frame
properties dialog box, clicking the Grids property page, and clicking New
Grid. This has one of two actions.

If the ArcMap 'Use wizards if available' option
is selected, the Grids and Graticules wizard is
displayed. This allows the user to select the type and
properties of the new grid. However, this action cannot be
extended to work with your custom grid, as the
wizard is hard coded.

If the 'Use wizards if available' option is not selected,
the Reference System Selector dialog box is displayed instead, allowing you to
select a predefined grid from those stored in the StyleGalleries and to edit the details of a grid by clicking
Properties.

If you have previously stored a ClippableIndexGrid
StyleItem in a referenced StyleGallery, then you will be able to select this
grid and alter its properties. However, the dialog box does not allow you to
create a new ClippableIndexGrid from scratch.

If you do want to provide a way to create a new
ClippableIndexGrid from the Grid's property page, see the section Creating the
NewClippableGridPage.

Alternatively, a user can create a grid, either based on
an existing grid in a StyleGallery or from scratch, by using the Style Manager dialog box.

To open the Style Manager in ArcMap, click Tools, Styles,
then Style Manager. To create a new grid, click the Reference Systems folder.
Then, to create a grid based on an existing StyleItem, click an existing grid. Alternatively, to create a new
grid from scratch based on a grid type, right-click
the left-hand pane, and click New from the
context menu.

This list of options for a new grid is taken from the
MapGridFactory classes currently
registered to the ESRI Map Grid Factories component
category.

So, to allow user access to create a new
ClippableIndexGrid, you will now create an
accompanying grid factory object class.

Creating a ClippableIndexGridFactory

By reviewing the ArcMap object model diagram, you can see
that the existing map grid factories inherit from the abstract MapGridFactory
abstract class and implement only one interfaceIMapGridFactory.

To solve the requirements of this example, you will
create a class that is a subtype of MapGridFactory called
ClippableIndexGridFactory.

Once the ClippableIndexGridFactory is registered to the
ESRI Map Grid Factories component category, a user will be able to create a new
ClippableIndexGrid from the Style Manager dialog box.

Create a map grid factory to allow users to create new
ClippableIndexGrids in the Style Manager.

Implementing IMapGridFactory

IMapGridFactory has one property and one method. The
read-only Name property should return the name of the type of grid the
factory creates. In this example it returns "Clippable Index Grid".

Once your custom map grid is built and registered, you will see
this name on the context menu when you attempt to create a new grid
in the Style Manager dialog box.

In the Create method, you should create a new instance of
the ClippableIndexGrid coclass and call the IMapGrid::SetDefaults method to set
the default properties of the MapGrid. Then return this new grid to the caller.

Create will be called when the user selects
ClippableIndexGrid from the new grid context menu in the Style Manager dialog box.

The ClippableIndexGridFactory creates a new
ClippableIndexGrid in its IMapGridFactory::Create method.

Creating a property page for the ClippableIndexGrid

When you attempt to edit the properties of any map grid, the
Reference System dialog box appears. When this dialog box is displayed, it
will interrogate all the property pages currently registered to the ESRI
Map Grids Property Pages component category and will display all those
pages that apply to the type of map grid being edited.

As your custom map grid class implements IMapGrid, the
dialog box will contain the existing Axes, Labels, and Lines property pages
(IPropertyPageContext::Applies for these pages will return True if passed any
class that implements IMapGrid.)

The ClippableIndexGrid also implements IIndexGrid; therefore, the Index property page will also be displayed.

At this point, a user will be able to change all the
properties of a ClippableIndexGrid, except IClippableIndexGrid::ClipGeometrythe
one property that is not available via the existing property pages.

Creating the ClippableGridPage

Add to your project a simple property page to allow users
to set the clip geometry of a ClippableIndexGrid. Add to the dialog box a
button called Use Selected Data Graphic, allowing the user to
set the value of the
clip geometry equal to the geometry of the currently selected graphic
element.

Once you have registered this property page to the ESRI
Map Grids Property Pages component category, it will appear in the Reference
System dialog box when the user has selected a ClippableIndexGrid.

Implementing IPropertyPage for the ClippableGridPage

ClippableGridPage is a standard implementation of a property page.
See 'Creating Property Pages' in Chapter 2 for more information on
implementing a property page.

In the Applies method, iterate through the objects
referenced by the SafeArray parameter and return True if you find an object that
implements IIndexGrid and IClippableIndexGrid.

The Use Selected Data Graphic button allows the user to set the shape of
the ClippableIndexGrid.

As the clip geometry must be a Polygon, check the type of
this graphic element. Then set the ClipGeometry property of the
ClippableIndexGrid you received in SetObjects to the Geometry of the graphic element.

Set the IClippableIndexGrid::ClipGeometry property from the
ElementSelection of the Map.

A User Interface for creating new custom map grids

Previously, you saw you cannot create a new
custom map grid from the Data Frame Properties dialog box via the Grids and Graticules wizard.

The Grids and Graticules wizard is not extensibleyou cannot add your
custom grid to this wizard.

You can still allow the creation of a new custom map grid from the
Data Frame Properties dialog box by adding a new property page that
has this functionality to the dialog box.

Although this is a slightly nonstandard way to extend the
framework, this technique does show you the flexibility of property pages used
in conjunction with component categories.

Creating the NewClippableGridPage

You will create a simple property page,
NewClippableGridPage, to allow users to add a new ClippableIndexGrid to a Map. This property
page will appear in the Data Frame Properties dialog box, as you will
register it to the ESRI Map Property Pages component category.

The NewClippableGridPage property page is shown hereit is a
standard implementation of a property page (again, see the
'Creating Property Pages' section in Chapter 2).

The check box at the top is unchecked by
default. When checked, it enables the remainder of
the dialog box's controls.

You can set a name for the grid and change
the number of columns and rows in the
grid. These changes are stored as simple member
variables while the page is displayed.

There is also a dropdown list box that allows you
to choose from a number of options for the tab
style of the labels for the grid; these are hard-coded
in this example, but could be identified at run
time from the Grid Labels component category. This selection is also stored as a member variable.

Last, there is also a button that allows you to set the
clip geometry of the ClippableIndexGrid to equal the currently selected graphic.
The code behind this button is similar to that shown for the ClippableGridPage
previouslythe geometry of the graphic is stored as a member variable.

Implementing IPropertyPage for the NewClippableIndexGrid

In the Applies method, instead of iterating through the
array passed in and checking for a particular type of object, simply return
True. You want the NewClippableGridPage to always appear in the Data
Frame Properties dialog box, regardless of the properties.

In the SetObjects method, check the array of objects
passed inyou should receive a reference to a Map. Store this reference as a
member variable; you will add your grid to this Map later in the Apply
method.

The Apply method should also add the new ClippableIndexGrid
to the MapFrame.

Note that the code here assumes it is running inside the
ArcMap process and uses the AppRef object. If there is a chance that your
property page may be used outside ArcMap, using AppRef may cause errors. You
may want to refer to Chapter 2, 'Developing Objects', for information on a
technique to avoid the instantiation of AppRef outside the ArcGIS applications.

Using AppRef may cause errors if your code finds itself running in a
process outside ArcMap.

Finally, add the ClippableIndexGrid and refresh the view to show
your new grid.