How to create SOE property pages


Summary This topic discusses how to create property pages for ArcCatalog and Manager to administer server object extension (SOE) properties and capabilities.

In this topic


Creating the SOE property page

In this section of the implementation, you will create the necessary objects to implement two custom property pages—one for ArcCatalog and one for ArcGIS Server Manager. Each of these pages allows you to configure your SOE's properties. The following are the properties for this SOE:
  • LayerName—Name of the layer in the MapServer's map document that contains the features on which to perform the analysis.
  • FieldName—Name of the field in the layer for which the clipped polygon's area is summarized.
Both of these properties can be set using either property page. Each property page shows all feature layers on the default map and fields for the selected feature layer. The property pages are only used by the geographic information system (GIS) server administrator to specify the properties for the custom SOE when the extension is enabled on a map service (MapServer server object).

Creating a solution folder for the property page

The solution for the custom SOE contains projects that handle several different implementation aspects (for example, implementation, registration, and administration). To keep the extension logic organized, add the property page classes in a new solution folder.
Do the following steps to create a solution folder for the property page:
  1. In the Solution Explorer, right-click the ArcGIS_Spatial_Query_SOE_CSharp2008 solution and select Add, New Solution Folder.
  2. Name the new folder ArcCatalog and Manager Integration. This folder is where you create the projects that define the custom property pages for ArcCatalog and ArcGIS Server Manager. See the following screen shot:

Implementing the ArcCatalog property page

Custom pages to define the properties of custom SOEs can be integrated with ArcCatalog. These custom pages are available in ArcCatalog when editing the capabilities of an ArcGIS Server service. This section shows how to implement such a property page for the SpatialQuerySOE extension.

Creating the SOE ArcCatalog property page project

Do the following steps to create a project that contains the implementation of an ArcCatalog property page:
  1. In the Solution Explorer, right-click the ArcCatalog and Manager Integration folder, click Add, then click New Project. The Add New Project dialog box appears.
  2. Under the Project types pane, click the Visual C# project type.
  3. Under the Templates pane, click Class Library.
  4. Type SpatialQuerySOE.ArcCatalog_CSharp in the Name text box.
  5. Click OK. In the Solution Explorer, right-click Class1.cs and select Delete.
  6. Right-click the project in the Solution Explorer and select Properties.
  7. On the Application tab, specify an assembly name and default namespace of SpatialQuerySOE.ArcCatalog.

Creating the ArcCatalog property page classes and form

This project contains two class files and a Windows form. The Windows form provides the visual interface of the property page in ArcCatalog. The class files provide the logic for registering the property page, showing and hiding the property page, then retrieving and setting SOE properties in the server object configuration file.
Do the following steps to create the ArcCatalog property page classes and form:
  1. In the Solution Explorer, right-click the SpatialQuerySOE.ArcCatalog_CSharp project, click Add, then click New Item. The Add New Item dialog box appears. 
  2. Under the Templates pane, click Class.
  3. Type SOEPropertyPage.cs in the Name text box.
  4. Click Add. A new class is added to the project and code for the class appears with automatically generated code. 
  5. In the Solution Explorer, right-click the SpatialQuerySOE.ArcCatalog_CSharp project, click Add, then click New Item. The Add New Item dialog box appears. 
  6. Under the Templates pane, click Class.
  7. Type PropertyPage.cs in the Name text box.
  8. Click Add. A new class is added to the project and code for the class appears with automatically generated code. Delete the automatically generated code. 
  9. In the Solution Explorer, right-click the SpatialQuerySOE.ArcCatalog_CSharp project, click Add, then click New Item. The Add New Item dialog box appears. 
  10. Under the Templates pane, click Windows Form.
  11. Type PropertyForm.cs in the Name text box.
  12. Click Add. A new Windows form is added to the project.

Implementing the form for the ArcCatalog property page

You will need to add references to assemblies that contain the required ArcObjects for implementation code in the property page classes and form.
Do the following steps to implement the form for the ArcCatalog property page:
  1. Add references to the following assemblies:
    • ESRI.ArcGIS.Carto
    • ESRI.ArcGIS.Catalog
    • ESRI.ArcGIS.CatalogUI
    • ESRI.ArcGIS.Framework
    • ESRI.ArcGIS.Geodatabase
    • ESRI.ArcGIS.Geometry
    • ESRI.ArcGIS.Server
    • ESRI.ArcGIS.System
  2. Do the following to add controls to the form in design view. These controls work with the Map document to return feature layer names and fields.
    1. In the Solution Explorer, double-click the PropertyForm.cs to open the form in design mode.
    2. On the properties for the form, click the FormBorderStyle drop-down list, then click None.
    3. Open the Toolbox pane, click the Windows Forms tab, click Label, then drag a label control on the form.
    4. Type Select the layer and summary field for the extension: in the Text property on the Property dialog box for the label.
    5. Drag two additional label controls on the form and set the text properties to Layer and Field.

      See the following screen shot that shows the label and the additional label controls on the form:


    6. Drag a ComboBox control onto the form. In the properties, type ComboLayers for the name.
    7. Drag another combo box onto the form. In the properties, type ComboFields for the name. 
  3. Do the following steps to add code to the form:
    1. In the Solution Explorer, right-click the form and click View Code to open the code window for the form.
    2. The code already contains a constructor and the class is defined within the SpatialQuerySOE.ArcCatalog namespace. Add logic to the constructor to determine if the service being configured is stopped. If it is not, it cannot be modified; therefore, disable the layers and fields combo boxes. See the following code example:
[C#]
namespace SpatialQuerySOE.ArcCatalog
{
    public partial class PropertyForm: Form
    {
        public PropertyForm()
        {
            InitializeComponent();
            // Get ArcCatalog's representation of the map service being modified.
            System.Type type = System.Type.GetTypeFromCLSID(typeof
                (ESRI.ArcGIS.Framework.AppRefClass).GUID);
            ESRI.ArcGIS.CatalogUI.IGxApplication gxApp = Activator.CreateInstance
                (type)as ESRI.ArcGIS.CatalogUI.IGxApplication;
            ESRI.ArcGIS.Catalog.IGxAGSObject gxAgsObj = gxApp.SelectedObject as
                ESRI.ArcGIS.Catalog.IGxAGSObject;
            // If the service is not stopped, disable the layers' and fields' drop-down lists so 
            //  the SOE cannot be configured.
            if (gxAgsObj.Status != "Stopped")
            {
                ComboLayers.Enabled = false;
                ComboFields.Enabled = false;
            }
        }
[VB.NET]
Namespace SpatialQuerySOE.ArcCatalog_VBNet
Partial Public Class PropertyForm
Inherits Form

Public Sub New()
    InitializeComponent()
    ' Get ArcCatalog's representation of the map service being modified.
    Dim Type As System.Type = System.Type.GetTypeFromCLSID(GetType(ESRI.ArcGIS.Framework.AppRefClass).GUID)
    Dim gxApp As ESRI.ArcGIS.CatalogUI.IGxApplication = TryCast(Activator.CreateInstance(Type), ESRI.ArcGIS.CatalogUI.IGxApplication)
    Dim gxAgsObj As ESRI.ArcGIS.Catalog.IGxAGSObject = TryCast(gxApp.SelectedObject, ESRI.ArcGIS.Catalog.IGxAGSObject)
    ' If the service is not stopped, disable the layers and fields drop-down lists so
    ' the SOE cannot be configured.
    If gxAgsObj.Status <> "Stopped" Then
        ComboLayers.Enabled = False
        ComboFields.Enabled = False
    End If
End Sub
    1. The form needs to expose properties to get and set the layer and field names the user chooses in the form, and to store the names of the layers and fields contained by the map document underlying the MapServer server object on which the SOE is enabled. The form also needs to track whether initialization logic or user interaction is the source of the selected layer being changed. Add the following member variables to the form to hold these properties:
[C#]
private string m_layer;
private string m_field;
private bool m_init = false;
private System.Collections.Generic.Dictionary m_fieldsDictionary = new
    System.Collections.Generic.Dictionary();
[VB.NET]
' The current SOE layer.
Private m_layer As String
' The current SOE field.
Private m_field As String
' Tracks whether the form is initializing.
Private m_init As Boolean = False
Private m_fieldsDictionary As New System.Collections.Generic.Dictionary(Of String, String())()
    1. Add a method to return the form handle, which is needed for the property page form to show its content in ArcCatalog. See the following code example:
[C#]
public int getHWnd()
{
    return this.Handle.ToInt32();
}
[VB.NET]
Friend Function getHWnd() As Integer
    Return Me.Handle.ToInt32()
End Function
    1. Add a property to store the ComPropertyPageSite of the class implementing the property page. ComPropertyPageSite is passed by ArcCatalog to the class implementing the property page, and by invoking the PageChanged method, can be used to notify ArcCatalog that the property page's contents have changed. The form's PageSite property is initialized by the class that implements the interfaces needed to integrate the property page with ArcCatalog. See the following code example:
[C#]
internal ESRI.ArcGIS.Framework.IComPropertyPageSite PageSite
{
    private get;
    set;
}
[VB.NET]
Private privatePageSite As ESRI.ArcGIS.Framework.IComPropertyPageSite
    1. Add the properties to get and set the selected layer and field name from the form. The get is accessed when ArcCatalog is used to create a server object and when changes are made to a server object via its property page. The set is needed to show properties for an existing server object when the form displays on the server object's property page. See the following code example:
[C#]
internal string Layer
{
    get
    {
        return m_layer;
    }
    set
    {
        m_layer = value;
    }
}

internal string Field
{
    get
    {
        return m_field;
    }
    set
    {
        m_field = value;
    }
}
[VB.NET]
Friend Property Layer() As String
    Get
    Return m_layer
    End Get
    Set(ByVal Value As String)
    m_layer = Value
    End Set
End Property


Friend Property Field() As String
    Get
    Return m_field
    End Get
    Set(ByVal Value As String)
    m_field = Value
    End Set
End Property
    1. Define the method to set the map document the SOE uses. The SOE uses the same map document associated with the MapServer server object on which the custom SOE is enabled. In the method, add code to open the map document, get all simple polygon feature layers, add the names to the ComboLayers combo box, and store the field names contained in the layers. The code also sets the layer combo box's selected layer to that currently specified by the SOE or if the extension has not been configured, to the first layer in the list. See the following code example:
[C#]
internal void SetMap(string filePath)
{
    // Open the map document.
    ESRI.ArcGIS.Carto.IMapDocument mapDocument = new
        ESRI.ArcGIS.Carto.MapDocumentClass();
    mapDocument.Open(filePath, null);
    // The call to get_Map() creates a lock (ldb) on a personal geodatabase. Make sure the
    // ArcCatalog user and ArcGIS Server container account has read and write 
    // access to data location. If not, feature classes in the personal geodatabase might 
    // not be accessible (or visible on the map).
    ESRI.ArcGIS.Carto.IMap map = mapDocument.get_Map(0);
    // Get IGeoFeatureLayers from the map.
    ESRI.ArcGIS.esriSystem.UID id = new ESRI.ArcGIS.esriSystem.UIDClass();
    id.Value = "{E156D7E5-22AF-11D3-9F99-00C04F6BC78E}";
    ESRI.ArcGIS.Carto.IEnumLayer enumLayer = map.get_Layers(id, true);
    int selectedIndex = 0;
    string[] fieldNames = null;
    m_fieldsDictionary.Clear();
    // Add the names of simple polygon feature layers to the layers' drop-down list and 
    // store the names of each layer's fields in a dictionary. 
    ESRI.ArcGIS.Carto.IFeatureLayer featureLayer = null;
    while ((featureLayer = enumLayer.Next()as ESRI.ArcGIS.Carto.IFeatureLayer) !=
        null)
    {
        if (featureLayer.FeatureClass.ShapeType ==
            ESRI.ArcGIS.Geometry.esriGeometryType.esriGeometryPolygon &&
            featureLayer.FeatureClass.FeatureType ==
            ESRI.ArcGIS.Geodatabase.esriFeatureType.esriFTSimple)
            ComboLayers.Items.Add(featureLayer.Name);
        if (featureLayer.Name == m_layer)
            selectedIndex = ComboLayers.Items.Count - 1;
        fieldNames = new string[featureLayer.FeatureClass.Fields.FieldCount];
        for (int i = 0; i < fieldNames.Length; i++)
            fieldNames[i] = featureLayer.FeatureClass.Fields.get_Field(i).Name;
        m_fieldsDictionary.Add(featureLayer.Name, fieldNames);
    }
    mapDocument.Close();
    mapDocument = null;
    map = null;
    // Toggle the init flag and initialize the layers' drop-down list to show the current 
    // SOE layer. Init is used to prevent the drop-down's SelectedIndexChanged logic 
    // from setting m_field during initialization.
    m_init = true;
    ComboLayers.SelectedIndex = selectedIndex;
}
[VB.NET]
Friend Sub SetMap(ByVal filePath As String)
    ' Open the map document.
    Dim mapDocument As ESRI.ArcGIS.Carto.IMapDocument = New ESRI.ArcGIS.Carto.MapDocumentClass()
    mapDocument.Open(filePath, Nothing)
    ' The call to get_Map() creates a lock (ldb) on a personal gdb. Make sure ArcCatalog user and
    ' ArcGIS Server container account has read and write access to data location. If not,
    ' feature classes in the personal gdb might not be accessible (or visible in the map).
    Dim map As ESRI.ArcGIS.Carto.IMap = mapDocument.Map(0)
    ' Get IGeoFeatureLayers from the map.
    Dim id As ESRI.ArcGIS.esriSystem.UID = New ESRI.ArcGIS.esriSystem.UIDClass()
    id.Value = "{E156D7E5-22AF-11D3-9F99-00C04F6BC78E}"
    Dim enumLayer As ESRI.ArcGIS.Carto.IEnumLayer = map.Layers(id, True)
    Dim selectedIndex As Integer = 0
    Dim fieldNames() As String = Nothing
    m_fieldsDictionary.Clear()
    ' Add the names of simple polygon feature layers to the layers drop-down and store the names
    ' of each layer's fields in a dictionary.
    Dim featureLayer As ESRI.ArcGIS.Carto.IFeatureLayer = Nothing
    featureLayer = TryCast(enumLayer.Next(), ESRI.ArcGIS.Carto.IFeatureLayer)
    Do While featureLayer IsNot Nothing
        If featureLayer.FeatureClass.ShapeType = ESRI.ArcGIS.Geometry.esriGeometryType.esriGeometryPolygon AndAlso featureLayer.FeatureClass.FeatureType = ESRI.ArcGIS.Geodatabase.esriFeatureType.esriFTSimple Then
            ComboLayers.Items.Add(featureLayer.Name)
        End If
        If featureLayer.Name = m_layer Then
            selectedIndex = ComboLayers.Items.Count - 1
        End If
        fieldNames = New String(featureLayer.FeatureClass.Fields.FieldCount - 1) {}
        For i As Integer = 0 To fieldNames.Length - 1
            fieldNames(i) = featureLayer.FeatureClass.Fields.Field(i).Name
        Next i
        m_fieldsDictionary.Add(featureLayer.Name, fieldNames)
        featureLayer = TryCast(enumLayer.Next(), ESRI.ArcGIS.Carto.IFeatureLayer)
    Loop
    mapDocument.Close()
    mapDocument = Nothing
    map = Nothing
    ' Toggle the init flag and initialize the layers drop-down list to show the current SOE layer.
    ' Init is used to prevent the drop-down's SelectedIndexChanged logic from setting m_field
    ' during initialization.
    m_init = True
    ComboLayers.SelectedIndex = selectedIndex
End Sub
    1. Add code to the ComboLayers' SelectedIndexChanged event to update the m_layer member variable with the name of the selected layer, get the field names for the selected layer, then display them in the ComboFields combo box.
      1. Open the form in design mode.
      2. Select the ComboLayers combo box.
      3. On the properties window, click the Events button.
      4. Double-click the SelectedIndexChanged event. This adds an event and opens the code window to the implementation of the event.

        When the user selects a layer (other than the one currently selected) from the ComboLayers drop-down list, the code in the SelectedIndexChanged event is executed. Add code to this event to update the m_layer member variable with the name of the selected layer, get the names of the selected layer's fields from the m_fieldsDictionary member variable, then add them to the ComboFields combo box. See the following code example:
[C#]
private void ComboLayers_SelectedIndexChanged(object sender, EventArgs e)
{
    m_layer = ComboLayers.Text;
    ComboFields.Items.Clear();
    // Add fields for the currently selected layer to the fields' drop-down list.
    int selectedIndex = 0;
    string[] fieldNames = m_fieldsDictionary[m_layer];
    for (int i = 0; i < fieldNames.Length; i++)
    {
        ComboFields.Items.Add(fieldNames[i]);
        // Get the index of the current field if the method is executing during initialization 
        //  and the current field matches the SOE field.
        if (m_init && fieldNames[i] == m_field)
            selectedIndex = ComboFields.Items.Count - 1;
    }
    // Update the field and layer properties if executing during initialization; otherwise, toggle the
    // init Boolean to indicate initialization is complete.
    if (m_init)
        m_init = false;
    // Set the fields' drop-down list to show the current SOE field.
    ComboFields.SelectedIndex = selectedIndex;
}
[VB.NET]
Private Sub ComboLayers_SelectedIndexChanged(ByVal sender As Object, ByVal e As EventArgs) Handles ComboLayers.SelectedIndexChanged
    m_layer = ComboLayers.Text
    ComboFields.Items.Clear()
    ' Add the fields for the currently selected layer to the fields drop-down list.
    Dim selectedIndex As Integer = 0
    Dim fieldNames() As String = m_fieldsDictionary(m_layer)
    For i As Integer = 0 To fieldNames.Length - 1
        ComboFields.Items.Add(fieldNames(i))
        ' Get the index of the current field if the method is executing during initialization and
        ' the current field matches the SOE field.
        If m_init AndAlso fieldNames(i) = m_field Then
            selectedIndex = ComboFields.Items.Count - 1
        End If
    Next i
    ' Update the field and layer properties if executing during initialization; otherwise, toggle the
    ' init Boolean to indicate initialization is complete.
    If m_init Then
        m_init = False
    End If
    ' Set the fields drop-down list to show the current SOE field.
    ComboFields.SelectedIndex = selectedIndex
End Sub
    1. Add code to the ComboFields combo box SelectedIndexChanged event to update the m_field member variable with the name of the selected field and fire the form's PageSite PageChanged method. Firing the PageChanged method notifies ArcCatalog that the page's contents have changed and enables the Apply button.
      1. Open the form in design mode.
      2. Select the ComboFields combo box.
      3. In the properties, click the Events button.
      4. Double-click the SelectedIndexChanged event. This adds an event and opens the code window to the implementation of the event.

        When the user clicks the ComboFields drop-down list to select a field, the code in the SelectedIndexChanged event is executed. Add code to this event to set the m_field member variable to the name of the selected field. See the following code example:
         
[C#]
private void ComboFields_SelectedIndexChanged(object sender, EventArgs e)
{
    // Update the current SOE field.
    m_field = ComboFields.Text;
    // Notify ArcCatalog that the properties have changed.
    this.PageSite.PageChanged();
}
[VB.NET]
Private Sub ComboFields_SelectedIndexChanged(ByVal sender As Object, ByVal e As EventArgs) Handles ComboFields.SelectedIndexChanged
    ' Update the current SOE field.
    m_field = ComboFields.Text
    ' Notify ArcCatalog that the properties have changed.
    Me.PageSite.PageChanged()
End Sub

Implementing the ArcCatalog property page classes

When the SpatialQuerySOE.ArcCatalog_CSharp project was created, two class files— SOEPropertyPage.cs and PropertyPage.cs—were created to house the logic for the classes that implement the ArcCatalog property page. SOEPropertyPage will be used to create an abstract class that implements the necessary interfaces and performs Component Object Model (COM) registration, while PropertyPage stores logic specific to the custom SpatialQuerySOE property page. Since many of the required interface members are not used in this case, it makes sense to abstract these away and simplify the implementation of the custom property page's logic. For similar simplification of other custom SOE property page implementations, the SOEPropertyPage class can also be used as a base class for those property pages.
References to the assemblies needed by the property page classes were added when implementing the property page's form.
Do the following steps to add code to the abstract property page base class:
  1. In the Solution Explorer, open the SOEPropertyPage.cs class file.
  2. Since this class will be consumed by a COM client (ArcCatalog), it needs to be registered as a COM object type. The GuidAttribute attribute defines a globally unique identifier (GUID) to identify the property page class via COM. For custom ArcCatalog SOE property pages, two interfaces must be implemented—IComPropertyPage and IAGSSOEParameterPage. See the following code example:
[C#]
namespace SOEUtilities
{
    [System.Runtime.InteropServices.GuidAttribute(
        "5514323A-02F8-48d3-B7BA-5BF07AD36F49")]
    public abstract class SOEPropertyPage: ESRI.ArcGIS.Framework.IComPropertyPage,
        ESRI.ArcGIS.CatalogUI.IAGSSOEParameterPage
    {
[VB.NET]
Namespace SOEUtilities
<System.Runtime.InteropServices.Guid("B1F2FD28-04AB-4653-8C5F-7C65B355AC1D")> _
                                     Public MustInherit Class SOEPropertyPage
    Implements ESRI.ArcGIS.Framework.IComPropertyPage, ESRI.ArcGIS.CatalogUI.IAGSSOEParameterPage
  1. For the property page to be accessible from ArcCatalog as an extension to ArcGIS Server object capabilities, register it in the AGS Extension Parameter Pages component category. This registration takes place when the property page class is registered with COM. A set of COM specific methods to register this property page in the appropriate category are shown in the following code example. The string parameter passed to both methods is the GUID defined for the custom property page. When registering the property page, the GUID associated with AGS Extension Parameter Pages component category (A585A585-B58B-4560-80E3-87A411859379) is added to the Implemented Categories key in the registry. When completed, ArcCatalog recognizes that the custom page is associated with ArcGIS Server extension parameter pages. When the property page is unregistered, the custom property page's GUID is removed from the registry. Since SOEPropertyPage is an abstract class, classes that inherit from SOEPropertyPage are registered, rather than SOEPropertyPage.
[C#]
// Automatically register with ArcCatalog as a component, use categories.exe -> 
// AGS Extension Parameter Pages to view. 
[System.Runtime.InteropServices.ComRegisterFunction()] static void RegisterFunction
    (String regKey)
{
    Microsoft.Win32.Registry.ClassesRoot.CreateSubKey(regKey.Substring(18) + 
        "\\Implemented Categories\\" + "{A585A585-B58B-4560-80E3-87A411859379}");
}

[System.Runtime.InteropServices.ComUnregisterFunction()] static void
    UnregisterFunction(String regKey)
{
    Microsoft.Win32.Registry.ClassesRoot.DeleteSubKeyTree(regKey.Substring(18));
}
[VB.NET]
<System.Runtime.InteropServices.ComRegisterFunction(), System.Runtime.InteropServices.ComVisible(False)> _
                                                    Private Shared Sub RegisterFunction(ByVal registerType As Type)
    Dim regKey As String = String.Format("HKEY_CLASSES_ROOT\CLSID\{{{0}}}", registerType.GUID)
    ESRI.ArcGIS.ADF.CATIDs.AGSSOEParameterPages.Register(regKey)
End Sub

<System.Runtime.InteropServices.ComUnregisterFunction(), System.Runtime.InteropServices.ComVisible(False)> _
                                                      Private Shared Sub UnregisterFunction(ByVal registerType As Type)
    Dim regKey As String = String.Format("HKEY_CLASSES_ROOT\CLSID\{{{0}}}", registerType.GUID)
    ESRI.ArcGIS.ADF.CATIDs.AGSSOEParameterPages.Unregister(regKey)
End Sub
To view property pages registered with ArcGIS component categories, run categories.exe in the <ArcGIS Install>\bin directory and select the applicable category. See the following screen shot:


  1. Implement the IComPropertyPage and IAGSSOEParameterPage interfaces.
    • The IComPropertyPage interfaces define the framework for ArcCatalog to work with property pages, such as show and hide. Custom SOE property pages must implement logic for the following IComPropertyPage members—PageSite, Activate, Show, and Hide. Implement these as abstract members so that subclasses of SOEPropertyPage are forced to implement them. See the following code example:
[C#]
public abstract ESRI.ArcGIS.Framework.IComPropertyPageSite PageSite
{
    set;
}

public abstract int Activate();
public abstract void Show();
public abstract void Hide();
[VB.NET]
Public MustOverride WriteOnly Property PageSite() As ESRI.ArcGIS.Framework.IComPropertyPageSite Implements ESRI.ArcGIS.Framework.IComPropertyPage.PageSite
Public MustOverride Function Activate() As Integer Implements ESRI.ArcGIS.Framework.IComPropertyPage.Activate
Public MustOverride Sub Show() Implements ESRI.ArcGIS.Framework.IComPropertyPage.Show
Public MustOverride Sub Hide() Implements ESRI.ArcGIS.Framework.IComPropertyPage.Hide
    • Two IComPropertyPage members—Height and Deactivate—can be implemented by custom SOE property pages (not required). Implement these as virtual members so that subclasses of SOEPropertyPage can implement these members (not required). See the following code example:
[C#]
public virtual int Height
{
    get
    {
        return 0;
    }
}

public virtual void Deactivate(){}
[VB.NET]
Public Overridable ReadOnly Property Height() As Integer Implements ESRI.ArcGIS.Framework.IComPropertyPage.Height
Get
Return 0
End Get
End Property

Public Overridable Sub Deactivate() Implements ESRI.ArcGIS.Framework.IComPropertyPage.Deactivate
End Sub
    • The remaining IComPropertyPage members are not used in custom SOE property page implementations. Explicitly implement these members without declaring them virtual so that SOEPropertyPage subclasses cannot implement them. See the following code example:
[C#]
public bool IsPageDirty
{
    get
    {
        return false;
    }
}

public string Title
{
    get
    {
        return null;
    }
    set{}
}

public int Priority
{
    get
    {
        return 0;
    }
    set{}
}

public string HelpFile
{
    get
    {
        return null;
    }
}

public int Width
{
    get
    {
        return 0;
    }
}

public void Apply(){}
public void Cancel(){}
public int get_HelpContextID(int controlID)
{
    return 0;
}

public void SetObjects(ESRI.ArcGIS.esriSystem.ISet objects){}
public bool Applies(ESRI.ArcGIS.esriSystem.ISet objects)
{
    return false;
}
[VB.NET]
Public ReadOnly Property IsPageDirty() As Boolean Implements ESRI.ArcGIS.Framework.IComPropertyPage.IsPageDirty
Get
Return False
End Get
End Property


Public Property Title() As String Implements ESRI.ArcGIS.Framework.IComPropertyPage.Title
    Get
    Return Nothing
    End Get
    Set(ByVal Value As String)
    End Set
End Property


Public Property Priority() As Integer Implements ESRI.ArcGIS.Framework.IComPropertyPage.Priority
    Get
    Return 0
    End Get
    Set(ByVal Value As Integer)
    End Set
End Property

Public ReadOnly Property HelpFile() As String Implements ESRI.ArcGIS.Framework.IComPropertyPage.HelpFile
Get
Return Nothing
End Get
End Property

Public ReadOnly Property Width() As Integer Implements ESRI.ArcGIS.Framework.IComPropertyPage.Width
Get
Return 0
End Get
End Property


Public Sub Apply() Implements ESRI.ArcGIS.Framework.IComPropertyPage.Apply
End Sub


Public Sub Cancel() Implements ESRI.ArcGIS.Framework.IComPropertyPage.Cancel
End Sub

Public ReadOnly Property HelpContextID(ByVal controlID As Integer) As Integer Implements ESRI.ArcGIS.Framework.IComPropertyPage.HelpContextID
Get
Return 0
End Get
End Property


Public Sub SetObjects(ByVal objects As ESRI.ArcGIS.esriSystem.ISet) Implements ESRI.ArcGIS.Framework.IComPropertyPage.SetObjects
End Sub


Public Function Applies(ByVal objects As ESRI.ArcGIS.esriSystem.ISet) As Boolean Implements ESRI.ArcGIS.Framework.IComPropertyPage.Applies
    Return False
End Function
    • The IAGSSOEParameterPage interface defines a set of properties that need to be explicitly implemented by a custom ArcGIS Server SOE. This interface works with server object and extension properties defined in this class and the map server object's configuration file to update the property page and the configuration file.

      The configuration properties for a server object are stored in the server object configuration file. Configuration files are named <service name>.<server object type>.cfg and stored on the server object manager (SOM) machine in the <ArcGIS Install>\server\user\cfg directory. For example, a map service named Yellowstone has a configuration file named Yellowstone.MapServer.cfg. Properties of SOEs configured to use with a server object are also stored in the cfg file. In this case, a set of properties associated with the custom SOE are written to the configuration file (LayerName and FieldName). In addition, three properties are read from the configuration file, two from the custom SOE (LayerName and FieldName) and one from the server object (FilePath).

      The ServerObjectProperties property stores the server object properties only, not the extensions. If the SOE has already been configured, the LayerName and FieldName properties are available. If not, only the path to the server object's map document is used. The setter portion of the ServerObjectProperties property will be called when the property page form opens.

      The ExtensionProperties property manages access to the properties of the SOE in the server object configuration file. In this case, if the SOE is enabled, the following code example is added to the server object configuration file:
[XML]
<Extension>
  <TypeName>SpatialQuerySOE</TypeName>
  <Enabled>true</Enabled>
  <Properties>
    <LayerName>Vegetation</LayerName>
    <FieldName>PRIMARY_</FieldName>
  </Properties>
  <Info>
    <WebEnabled>true</WebEnabled>
    <WebCapabilities></WebCapabilities>
  </Info>
</Extension>
Since all members of the IAGSSOEParameterPage interface must be implemented by custom SOE property pages, implement them as abstract members to require subclasses of SOEPropertyPage to implement them. See the following code example:
[C#]
public abstract ESRI.ArcGIS.esriSystem.IPropertySet ServerObjectProperties
{
    get;
    set;
}

public abstract ESRI.ArcGIS.esriSystem.IPropertySet ExtensionProperties
{
    get;
    set;
}

public abstract string ServerObjectExtensionType
{
    get;
}

public abstract string ServerObjectType
{
    get;
}
[VB.NET]
Public MustOverride Property ServerObjectProperties() As ESRI.ArcGIS.esriSystem.IPropertySet Implements ESRI.ArcGIS.CatalogUI.IAGSSOEParameterPage.ServerObjectProperties
Public MustOverride Property ExtensionProperties() As ESRI.ArcGIS.esriSystem.IPropertySet Implements ESRI.ArcGIS.CatalogUI.IAGSSOEParameterPage.ExtensionProperties
Public MustOverride ReadOnly Property ServerObjectExtensionType() As String Implements ESRI.ArcGIS.CatalogUI.IAGSSOEParameterPage.ServerObjectExtensionType
Public MustOverride ReadOnly Property ServerObjectType() As String Implements ESRI.ArcGIS.CatalogUI.IAGSSOEParameterPage.ServerObjectType
Do the following steps to implement the property page:
  1. In the Solution Explorer, open the PropertyPage.cs file.
  2. Since the required interfaces for custom SOE property pages have been implemented by the abstract class SOEPropertyPage, the property page class only needs to inherit from that class. See the following code example:
[C#]
namespace SpatialQuerySOE.ArcCatalog
{
    [System.Runtime.InteropServices.Guid("2EAD4A98-BB8C-4b88-A323-48F53653ACBF")]
    public class PropertyPage: SOEUtilities.SOEPropertyPage
    {
[VB.NET]
Namespace SpatialQuerySOE.ArcCatalog_VBNet
<System.Runtime.InteropServices.Guid("2378B5DC-BF24-498c-B0A2-6E8C43628EA3")> _
                                     Public Class PropertyPage
    Inherits SOEUtilities.SOEPropertyPage
  1. The constructor of the Property page class is called by ArcCatalog when the property page needs to be created and displayed. The constructor assigns member variables as follows:
    • propertyPage—New instance of the PropertyForm Windows form that defines the interface of the property page.
    • m_serverObjectType—Type of server object that the SOE is registered to use with, in this case, MapServer. Used as the return value for the ServerObjectType property.
    • m_extensionType—Name of the SOE's type, in this case, SpatialQuerySOE. This value must match the name of the SOE when it is registered with ArcGIS Server. Used as the return value for the ServerObjectExtensionType property.
    The destructor is explicitly defined to dispose of the property page. See the following code example:
[C#]
public PropertyPage()
{
    propertyPage = new PropertyForm();
    m_serverObjectType = "MapServer";
    // Must be the same name used when registering the SOE.
    m_extensionType = "SpatialQuerySOE";
}

~PropertyPage()
{
    propertyPage.Dispose();
    propertyPage = null;
}
[VB.NET]
Public Sub New()

    property_Page = New PropertyForm()
        m_serverObjectType = "MapServer"
        ' Must be the same name used when registering the SOE.
        m_extensionType = "SpatialQuerySOE_VBNet"
    End Sub

    Protected Overrides Sub Finalize()

    property_Page.Dispose()

        property_Page = Nothing
        End Sub
  1. Properties of the MapServer server object on which the SOE is enabled are passed to the property page through the setter of ServerObjectProperties. Since the property page form implementation retrieves layers and fields from the map document underlying the server object, the path to that document must be retrieved from the server object's properties and passed to the form. See the following code example:
[C#]
public override ESRI.ArcGIS.esriSystem.IPropertySet ServerObjectProperties
{
    get
    {
        return m_serverObjectProperties;
    }
    set
    {
        // Pass the location of the map document underlying the current 
        //  server object to the property form.
        m_serverObjectProperties = value;
        propertyPage.SetMap(m_serverObjectProperties.GetProperty("FilePath")
            .ToString());
    }
}
[VB.NET]
Public Overrides Property ServerObjectProperties() As ESRI.ArcGIS.esriSystem.IPropertySet
Get
Return m_serverObjectProperties
End Get
Set(ByVal Value As ESRI.ArcGIS.esriSystem.IPropertySet)
' Pass the location of the map document underlying the current server object
' to the property form.
m_serverObjectProperties = Value

property_Page.SetMap(m_serverObjectProperties.GetProperty("FilePath").ToString())
    End Set
End Property
  1. The LayerName and FieldName properties differ depending on changes made via the property page form. When the custom SOE's properties are applied in ArcCatalog (that is, the custom SOE is enabled and OK or Apply is clicked on the Properties dialog box), these properties are retrieved from the custom SOE property page through the getter of the ExtensionProperties property.

    The retrieved properties are added to or updated in the map server object's configuration file. Changes to the LayerName and FieldName properties can be saved as shown in the following code example. The saved SOE properties are passed to the property page implementation through the setter of the ExtensionProperties property; therefore, initialize the corresponding properties on the property page form there.
[C#]
public override ESRI.ArcGIS.esriSystem.IPropertySet ExtensionProperties
{
    get
    {
        m_extensionProperties.SetProperty("LayerName", propertyPage.Layer);
        m_extensionProperties.SetProperty("FieldName", propertyPage.Field);
        return m_extensionProperties;
    }
    set
    {
        m_extensionProperties = value;
        propertyPage.Layer = m_extensionProperties.GetProperty("LayerName").ToString
            ();
        propertyPage.Field = m_extensionProperties.GetProperty("FieldName").ToString
            ();
    }
}
[VB.NET]
Public Overrides Property ExtensionProperties() As ESRI.ArcGIS.esriSystem.IPropertySet
Get
' Checking the SOE in ArcCatalog adds the following properties into the
' map server .cfg file or existing properties are updated.
m_extensionProperties.SetProperty("LayerName", property_Page.Layer)
m_extensionProperties.SetProperty("FieldName", property_Page.Field)
Return m_extensionProperties
End Get
Set(ByVal Value As ESRI.ArcGIS.esriSystem.IPropertySet)
' Pass the extension properties to the property form.
m_extensionProperties = Value

property_Page.Layer = m_extensionProperties.GetProperty("LayerName").ToString()

    property_Page.Field = m_extensionProperties.GetProperty("FieldName").ToString()
        End Set
    End Property
  1. The member variables storing the SOE type and the server object type to which the extension applies are set when the property page class is instantiated, as previously shown. The implementations of the ServerObjectExtensionType and ServerObjectType properties return these member variables. See the following code example:
[C#]
public override string ServerObjectExtensionType
{
    get
    {
        return m_extensionType;
    }
}

public override string ServerObjectType
{
    get
    {
        return m_serverObjectType;
    }
}
[VB.NET]
Public Overrides ReadOnly Property ServerObjectExtensionType() As String
Get
Return m_extensionType
End Get
End Property

Public Overrides ReadOnly Property ServerObjectType() As String
Get
Return m_serverObjectType
End Get
End Property

Setting assembly properties for the SpatialQuerySOE.ArcCatalog_CSharp project

Do the following steps to set the assembly properties:
  1. In the Solution Explorer, open the AssemblyInfo.cs file for the SpatialQuerySOE.ArcCatalog_CSharp project.
  2. Modify the file to reflect the settings in the following code example:
[C#]
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
[assembly: AssemblyTitle("SpatialQuerySOE.ArcCatalog")][assembly:
    AssemblyDescription("")][assembly: AssemblyConfiguration("")][assembly:
    AssemblyCompany("ESRI")][assembly: AssemblyProduct("SpatialQuerySOE.ArcCatalog")
    ][assembly: AssemblyCopyright("Copyright © ESRI 2010")][assembly:
    AssemblyTrademark("")][assembly: AssemblyCulture("")][assembly: ComVisible(true)
    ][assembly: GuidAttribute("2597052D-C832-40db-9DFF-39D49BE60357")][assembly:
    AssemblyVersion("1.0.0.0")][assembly: AssemblyFileVersion("1.0.0.0")]
[VB.NET]
Imports Microsoft.VisualBasic
Imports System.Reflection
Imports System.Runtime.CompilerServices
Imports System.Runtime.InteropServices
<Assembly
AssemblyTitle("SpatialQuerySOE.ArcCatalog_VBNet")>
<Assembly
AssemblyDescription("")>
<Assembly
AssemblyConfiguration("")>
<Assembly
AssemblyCompany("ESRI")>
<Assembly
AssemblyProduct("SpatialQuerySOE.ArcCatalog_VBNet")>
<Assembly
AssemblyCopyright("Copyright © ESRI 2010")>
<Assembly
AssemblyTrademark("")>
<Assembly
AssemblyCulture("")>
<Assembly
ComVisible(True)>
<Assembly
AssemblyVersion("1.0.0.0")>
<Assembly
AssemblyFileVersion("1.0.0.0")>

Building the SOE ArcCatalog property page project

Do the following steps to build the property page project:
  1. Build the SpatialQuerySOE.ArcCatalog_CSharp project.
  2. Create the SpatialQuerySOE.ArcCatalog.dll assembly.

Integrating the SOE with ArcGIS Server Manager

Pages to define the properties of custom SOEs can also be created for the ArcGIS Server Manager Web administration application. This part of the topic shows how to do this with the SpatialQuerySOE custom extension. As with the property page for ArcCatalog, the property page for Manager allows interactive specification of the LayerName and FieldName properties.

Creating the ArcGIS Server Manager SOE property page project

Do the following steps to create a project that holds the implementation of the custom property panel:
  1. In the Solution Explorer, right-click the ArcCatalog and Manager Integration solution folder, click Add, then click New Project. The Add New Project dialog box appears.
  2. Under the Project types pane, select the Visual C# project type. 
  3. Under the Templates pane, click Class Library. Type SpatialQuerySOE.Manager_CSharp in the Name text box.
  4. Click OK.
  5. In the Solution Explorer, right-click the Class1.cs file and select Delete.
  6. Right-click the project in the Solution Explorer and select Properties.
  7. On the Application tab, specify an assembly name and default namespace of SpatialQuerySOE.Manager.
When finished, this project contains a class file that defines a property page to configure SpatialQuerySOE in Manager. The class contains the logic to define the property page's user interface (UI), retrieve the SOE’s properties, populate the UI with the properties, then update the SOE's properties from the UI.

Creating the ArcGIS Server Manager property page class

Do the following steps to create the property page class:
  1. In the Solution Explorer, right-click the SpatialQuerySOE.Manager_CSharp project. Click Add and click New Item. The Add New Item dialog box appears.
  2. Under the Templates pane, click Class.
  3. Type Configurator.cs in the Name text box.

Implementing the ArcGIS Server Manager property page class

Do the following steps to implement the property page class:
  1. The implementation code requires you to add references to several assemblies. Some of these contain the required ArcObjects for interacting with a service's map document and server object:
    • ESRI.ArcGIS.Carto
    • ESRI.ArcGIS.Geodatabase
    • ESRI.ArcGIS.Server
    • ESRI.ArcGIS.ServerManager
    • ESRI.ArcGIS.System
    Other references define Web controls and objects to use in building the page's UI:
    • System.Web
    • System.Web.Extensions

    Add references for all of these assemblies to the SpatialQuerySOE.Manager_CSharp project.
  2. Open the Configurator.cs file.
  3. For a class to be integrated in Manager, it must implement IServerObjectExtensionConfigurator. Change the class declaration to the following code example:
[C#]
namespace SpatialQuerySOE.Manager
{
    public class Configurator:
        ESRI.ArcGIS.ServerManager.IServerObjectExtensionConfigurator{}
}
[VB.NET]
Namespace SpatialQuerySOE.Manager_VBNet

Public Class Configurator
    Implements ESRI.ArcGIS.ServerManager.IServerObjectExtensionConfigurator
  1. Declare a set of member variables to store references to the following:
    • DropDownList that defines the extension’s layer (m_layersDropDown)
    • DropDownList that defines the extension's field (m_fieldsDropDown)
    • Name of the selected layer (m_layer)
    • Name of the selected field (m_field)
    • JavaScript Object Notation (JSON) string that contains the names of the current service’s layers and fields contained by each (m_jsonServiceLayersAndFields)
     
    See the following code example:
[C#]
private System.Web.UI.WebControls.DropDownList m_layersDropDown = new
    System.Web.UI.WebControls.DropDownList();
private System.Web.UI.WebControls.DropDownList m_fieldsDropDown = new
    System.Web.UI.WebControls.DropDownList();
private string m_layer;
private string m_field;
private string m_jsonServiceLayersAndFields = "{}";
[VB.NET]
Private m_layersDropDown As New System.Web.UI.WebControls.DropDownList()
Private m_fieldsDropDown As New System.Web.UI.WebControls.DropDownList()
Private m_layer As String
Private m_field As String
Private m_jsonServiceLayersAndFields As String = "{}"
  1. Define the LoadConfigurator method when implementing IServerObjectExtensionConfigurator. LoadConfigurator is called by Manager when the property page is loaded, and is responsible for returning a string containing the Hypertext Markup Language (HTML) that defines the property page's UI. The following are the arguments passed to this method:
    • serverContext—Server context for the service
    • ServerObjectProperties—Properties of the server object (that is, service)
    • ExtensionProperties—SOE properties for the current server object
    • InfoProperties—Global (that is, server level) SOE properties
    • isEnabled—Whether the extension is enabled on the current server object
    • servicesEndPoint—End point for the server’s Web services
    • serviceName—Name of the current service
     
    Stub out the method as shown in the following code example:
[C#]
public string LoadConfigurator(ESRI.ArcGIS.Server.IServerContext serverContext
    System.Collections.Specialized.NameValueCollection ServerObjectProperties,
    System.Collections.Specialized.NameValueCollection ExtensionProperties,
    System.Collections.Specialized.NameValueCollection InfoProperties, bool
    isEnabled, string servicesEndPoint, string serviceName){}
[VB.NET]
Public Function LoadConfigurator(ByVal serverContext As ESRI.ArcGIS.Server.IServerContext, ByVal ServerObjectProperties As System.Collections.Specialized.NameValueCollection, _
                                 ByVal ExtensionProperties As System.Collections.Specialized.NameValueCollection, ByVal InfoProperties As System.Collections.Specialized.NameValueCollection, ByVal isEnabled As Boolean, ByVal servicesEndPoint As String, ByVal serviceName As String) As String _
                                 Implements ESRI.ArcGIS.ServerManager.IServerObjectExtensionConfigurator.LoadConfigurator
    1. Verify the extension is enabled and if not, return an appropriate message. See the following code example:
[C#]
if (!isEnabled)
    return ("<span>No Properties to configure</span>");
[VB.NET]
If (Not isEnabled) Then
    Return ("<span>No Properties to configure</span>")
End If
    1. Initialize the member variables holding the extension's layer and field names based on the passed-in extension properties. See the following code example:
[C#]
if (!string.IsNullOrEmpty(ExtensionProperties["LayerName"]))
    m_layer = ExtensionProperties["LayerName"];
if (!string.IsNullOrEmpty(ExtensionProperties["FieldName"]))
    m_field = ExtensionProperties["FieldName"];
[VB.NET]
If (Not String.IsNullOrEmpty(ExtensionProperties("LayerName"))) Then
    m_layer = ExtensionProperties("LayerName")
End If
If (Not String.IsNullOrEmpty(ExtensionProperties("FieldName"))) Then
    m_field = ExtensionProperties("FieldName")
End If
    1. Declare and initialize the controls that comprise the property configuration UI. See the following code example:
[C#]
System.Web.UI.HtmlControls.HtmlGenericControl propertiesDiv = new
    System.Web.UI.HtmlControls.HtmlGenericControl("propertiesDiv");
propertiesDiv.Style[System.Web.UI.HtmlTextWriterStyle.Padding] = "10px";

System.Web.UI.HtmlControls.HtmlTable table = new
    System.Web.UI.HtmlControls.HtmlTable();
table.CellPadding = table.CellSpacing = 4;
propertiesDiv.Controls.Add(table);

System.Web.UI.HtmlControls.HtmlTableRow row = new
    System.Web.UI.HtmlControls.HtmlTableRow();
table.Rows.Add(row);

System.Web.UI.HtmlControls.HtmlTableCell cell = new
    System.Web.UI.HtmlControls.HtmlTableCell();
row.Cells.Add(cell);
cell.ColSpan = 2;

System.Web.UI.WebControls.Label lbl = new System.Web.UI.WebControls.Label();
lbl.Text = "Choose the layer and field.";
cell.Controls.Add(lbl);

row = new System.Web.UI.HtmlControls.HtmlTableRow();
table.Rows.Add(row);

cell = new System.Web.UI.HtmlControls.HtmlTableCell();
row.Cells.Add(cell);

lbl = new System.Web.UI.WebControls.Label();
cell.Controls.Add(lbl);
lbl.Text = "Layer:";

cell = new System.Web.UI.HtmlControls.HtmlTableCell();
row.Cells.Add(cell);
cell.Controls.Add(m_layersDropDown);
m_layersDropDown.ID = "layersDropDown";

m_layersDropDown.Attributes["onchange"] = 
    "ExtensionConfigurator.OnLayerChanged(this);";

row = new System.Web.UI.HtmlControls.HtmlTableRow();
table.Rows.Add(row);

cell = new System.Web.UI.HtmlControls.HtmlTableCell();
row.Cells.Add(cell);

lbl = new System.Web.UI.WebControls.Label();
cell.Controls.Add(lbl);
lbl.Text = "Fields:";

cell = new System.Web.UI.HtmlControls.HtmlTableCell();
row.Cells.Add(cell);
cell.Controls.Add(m_fieldsDropDown);
m_fieldsDropDown.ID = "fieldsDropDown";
[VB.NET]
'Container div and table.
Dim propertiesDiv As New System.Web.UI.HtmlControls.HtmlGenericControl("propertiesDiv")
propertiesDiv.Style(System.Web.UI.HtmlTextWriterStyle.Padding) = "10px"
Dim table As New System.Web.UI.HtmlControls.HtmlTable()
table.CellSpacing = 4
table.CellPadding = table.CellSpacing
propertiesDiv.Controls.Add(table)

' Header row.
Dim row As New System.Web.UI.HtmlControls.HtmlTableRow()
table.Rows.Add(row)
Dim cell As New System.Web.UI.HtmlControls.HtmlTableCell()
row.Cells.Add(cell)
cell.ColSpan = 2
Dim lbl As New System.Web.UI.WebControls.Label()
lbl.Text = "Choose the layer and field."
cell.Controls.Add(lbl)

' Layer drop-down row.
row = New System.Web.UI.HtmlControls.HtmlTableRow()
table.Rows.Add(row)
cell = New System.Web.UI.HtmlControls.HtmlTableCell()
row.Cells.Add(cell)
lbl = New System.Web.UI.WebControls.Label()
cell.Controls.Add(lbl)
lbl.Text = "Layer:"
cell = New System.Web.UI.HtmlControls.HtmlTableCell()
row.Cells.Add(cell)
cell.Controls.Add(m_layersDropDown)
m_layersDropDown.ID = "layersDropDown"
' Wire the OnLayerChanged JavaScript function (defined in SupportingJavaScript) to fire
' when a new layer is selected.
m_layersDropDown.Attributes("onchange") = "ExtensionConfigurator.OnLayerChanged(this);"

' Fields drop-down row.
row = New System.Web.UI.HtmlControls.HtmlTableRow()
table.Rows.Add(row)
cell = New System.Web.UI.HtmlControls.HtmlTableCell()
row.Cells.Add(cell)
lbl = New System.Web.UI.WebControls.Label()
cell.Controls.Add(lbl)
lbl.Text = "Fields:"
cell = New System.Web.UI.HtmlControls.HtmlTableCell()
row.Cells.Add(cell)
cell.Controls.Add(m_fieldsDropDown)
m_fieldsDropDown.ID = "fieldsDropDown"
    1. The onchange attribute for the layers DropDownList (m_layerDropDown) has been set to invoke ExtensionConfigurator.OnLayerChanged. Define this function when implementing the SupportingJavaScript method. To initialize the layers and field DropDownLists, add the following code example. Define the logic to populate the property page's UI with the current service layers and fields separately, in the populateDropDowns method.
[C#]
string fileName = ServerObjectProperties["FilePath"];
populateDropDowns(serverContext, fileName);
[VB.NET]
Dim fileName As String = ServerObjectProperties("FilePath")
populateDropDowns(serverContext, fileName)
    1. Now that the UI's components are created and initialized, convert them to an HTML string, then return. Insert the following code example:
[C#]
System.IO.StringWriter stringWriter = new System.IO.StringWriter();
System.Web.UI.HtmlTextWriter htmlWriter = new System.Web.UI.HtmlTextWriter
    (stringWriter);
propertiesDiv.RenderControl(htmlWriter);
string html = stringWriter.ToString();
stringWriter.Close();
return html;
[VB.NET]
Dim stringWriter As New System.IO.StringWriter()
Dim htmlWriter As New System.Web.UI.HtmlTextWriter(stringWriter)
propertiesDiv.RenderControl(htmlWriter)
Dim html As String = stringWriter.ToString()
stringWriter.Close()
Return html
  1. Implement the populateDropDowns method. Access the map document underlying the current service and get all layers contained in the document that implemented IGeoFeatureLayer. At the end of the method, in a finally block, add the following code example to close the map document and avoid locking conflicts:
[C#]
private void populateDropDowns(ESRI.ArcGIS.Server.IServerContext serverContext,
    string mapDocPath)
{
    ESRI.ArcGIS.Carto.IMapDocument mapDoc = null;
    ESRI.ArcGIS.Carto.IMap map = null;

    try
    {
        mapDoc = serverContext.CreateObject("esriCarto.MapDocument")as
            ESRI.ArcGIS.Carto.IMapDocument;
        mapDoc.Open(mapDocPath, null);
        map = mapDoc.get_Map(0);
        ESRI.ArcGIS.esriSystem.UID id = serverContext.CreateObject("esriSystem.UID")
            as ESRI.ArcGIS.esriSystem.UID;
        id.Value = "{E156D7E5-22AF-11D3-9F99-00C04F6BC78E}";
        ESRI.ArcGIS.Carto.IEnumLayer enumLayer = map.get_Layers(id, true);
    }
    catch {}
    finally
    {
        if (mapDoc != null)
            mapDoc.close();
        mapDoc = null;
    }
}
[VB.NET]
Private Sub populateDropDowns(ByVal serverContext As ESRI.ArcGIS.Server.IServerContext, ByVal mapDocPath As String)
    Dim mapDoc As ESRI.ArcGIS.Carto.IMapDocument = Nothing
    Dim map As ESRI.ArcGIS.Carto.IMap = Nothing
    
    ' Get the map underlying the current service.
    mapDoc = CType(serverContext.CreateObject("esriCarto.MapDocument"), ESRI.ArcGIS.Carto.IMapDocument)
    mapDoc.Open(mapDocPath, Nothing)
    map = mapDoc.Map(0)
    ' Get IGeoFeatureLayers from the map.
    Dim id As ESRI.ArcGIS.esriSystem.UID = CType(serverContext.CreateObject("esriSystem.UID"), ESRI.ArcGIS.esriSystem.UID)
    id.Value = "{E156D7E5-22AF-11D3-9F99-00C04F6BC78E}"
    Dim enumLayer As ESRI.ArcGIS.Carto.IEnumLayer = map.Layers(id, True)
    1. Since the SOE works with simple polygon layers, add logic to find these layers. The names of each layer are added to the layers' drop-down list and if the layer is the current extension layer or the first layer to be added, the names of the layer’s fields are added to the fields' drop-down list. Add each layer's name and fields to a Dictionary object for serialization to JSON. This JSON layer-fields mapping is used in client-tier logic. Add the following code example to the try block:
[C#]
ESRI.ArcGIS.Carto.IFeatureLayer featureLayer = (ESRI.ArcGIS.Carto.IFeatureLayer)
    enumLayer.Next();
Dictionary < string, List < string >> layersAndFieldsDictionary = new Dictionary <
    string, List < string >> ();
bool addFields = false;
while (featureLayer != null)
{
    List < string > fieldsList = new List < string > ();
    if (featureLayer.FeatureClass.ShapeType ==
        ESRI.ArcGIS.Geometry.esriGeometryType.esriGeometryPolygon &&
        featureLayer.FeatureClass.FeatureType ==
        ESRI.ArcGIS.Geodatabase.esriFeatureType.esriFTSimple)
    {
        m_layersDropDown.Items.Add(featureLayer.Name);
        if (featureLayer.Name == m_layer || (m_layer == null &&
            m_layersDropDown.Items.Count == 1)addFields = true;
            ESRI.ArcGIS.Geodatabase.IFields fields =
            featureLayer.FeatureClass.Fields; for (int i = 0; i < fields.FieldCount;
            i++)
        {
            ESRI.ArcGIS.Geodatabase.IField field = fields.get_Field(i);
                fieldsList.Add(field.Name); if (addFields)m_fieldsDropDown.Items.Add
                (field.Name); 
        }
        addFields = false; layersAndFieldsDictionary.Add(featureLayer.Name,
            fieldsList); 
    }
    featureLayer = (ESRI.ArcGIS.Carto.IFeatureLayer)enumLayer.Next(); 
}

System.Web.Script.Serialization.JavaScriptSerializer serializer = new
    System.Web.Script.Serialization.JavaScriptSerializer();
    m_jsonServiceLayersAndFields = serializer.Serialize(layersAndFieldsDictionary);
[VB.NET]
Dim featureLayer As ESRI.ArcGIS.Carto.IFeatureLayer = CType(enumLayer.Next(), ESRI.ArcGIS.Carto.IFeatureLayer)
Dim layersAndFieldsDictionary As New Dictionary(Of String, List(Of String))()
Dim addFields As Boolean = False
Do While featureLayer IsNot Nothing
    Dim fieldsList As New List(Of String)()
    ' Check whether the current layer is a simple polygon layer.
    If featureLayer.FeatureClass.ShapeType = ESRI.ArcGIS.Geometry.esriGeometryType.esriGeometryPolygon AndAlso featureLayer.FeatureClass.FeatureType = ESRI.ArcGIS.Geodatabase.esriFeatureType.esriFTSimple Then
        ' Add the layer to the layers drop-down.
        m_layersDropDown.Items.Add(featureLayer.Name)
        ' Check whether the fields drop-down should be initialized with fields from the current
        ' loop layer.
        If featureLayer.Name = m_layer OrElse (m_layer Is Nothing AndAlso m_layersDropDown.Items.Count = 1) Then
            addFields = True
        End If
        ' Add each field to the fields list.
        Dim fields As ESRI.ArcGIS.Geodatabase.IFields = featureLayer.FeatureClass.Fields
        For i As Integer = 0 To fields.FieldCount - 1
            Dim field As ESRI.ArcGIS.Geodatabase.IField = fields.Field(i)
            fieldsList.Add(field.Name)
            ' If the current loop layer is the first, add its fields to the fields drop-down.
            If addFields Then
                m_fieldsDropDown.Items.Add(field.Name)
            End If
        Next i
        addFields = False
        ' Add the layer name and its fields to the dictionary.
        layersAndFieldsDictionary.Add(featureLayer.Name, fieldsList)
    End If
    featureLayer = CType(enumLayer.Next(), ESRI.ArcGIS.Carto.IFeatureLayer)
Loop
' Serialize the dictionary containing the layer and field names to JSON.
Dim serializer As New System.Web.Script.Serialization.JavaScriptSerializer()
m_jsonServiceLayersAndFields = serializer.Serialize(layersAndFieldsDictionary)
    1. The last step to populate the drop-down lists is setting the layer and field in the layers' and fields' drop-down list if these have already been defined on the current server object. See the following code example:
[C#]
if (m_layer != null)
    m_layersDropDown.SelectedValue = m_layer;
if (m_field != null)
    m_fieldsDropDown.SelectedValue = m_field;
[VB.NET]
If m_layer IsNot Nothing Then
    m_layersDropDown.SelectedValue = m_layer
End If
' If a field is defined for the extension, select it in the fields drop-down.
If m_field IsNot Nothing Then
    m_fieldsDropDown.SelectedValue = m_field
End If
  1. The next IServerObjectExtensionConfigurator member that must be implemented is a read-only property called SupportingJavaScript. This method can be used to return a string of JavaScript to execute when the property page is loaded. In this implementation, use this method to define the ExtensionConfigurator.OnLayerChanged function. In LoadConfigurator, you specified that this method be invoked when a layer is selected from the layers' drop-down list.

    ExtensionConfigurator is a global JavaScript object defined by the Manager application and available to custom SOE property pages. This implementation of SupportingJavaScript uses the JSON formatted layer-fields mapping. See the following code example:
[C#]
public string SupportingJavaScript
{
    get
    {
        return string.Format(@"var layerFieldsMapping = 
        {
            0
        }; ExtensionConfigurator.OnLayerChanged = function(layersDropDown)
        {
            {
                var layerName =
                    layersDropDown.options[layersDropDown.selectedIndex].value; var
                    fieldsDropDown = document.getElementById('fieldsDropDown'); var
                    len = fieldsDropDown.options ? fieldsDropDown.options.length :
                    0; for (var i = 0; i < len; i++)fieldsDropDown.remove(0); var
                    fieldsArray = layerFieldsMapping[layerName]; if (fieldsArray)
                {
                    {
                        for (i = 0; i < fieldsArray.length; i++)
                            fieldsDropDown.options.add(new Option(fieldsArray[i],
                            fieldsArray[i])); fieldsDropDown.options[0].selected =
                            true; 
                    }
                }
            }
        }
        ", m_jsonServiceLayersAndFields)
            ;
    }
}
[VB.NET]
Public ReadOnly Property SupportingJavaScript() As String Implements ESRI.ArcGIS.ServerManager.IServerObjectExtensionConfigurator.SupportingJavaScript
Get
Return String.Format("" & ControlChars.CrLf & _
                     "// JSON object storing the names of the current service's layers and their fields" & ControlChars.CrLf & _
                     "var layerFieldsMapping = {0};" & ControlChars.CrLf & ControlChars.CrLf & _
                     "// ExtensionConfigurator is a global JavaScript object defined by Manager and available to SOE property page" & ControlChars.CrLf & _
                     "// implementations. Here we use it to define the client logic to execute when a new layer is selected." & ControlChars.CrLf & _
                     "ExtensionConfigurator.OnLayerChanged = function(layersDropDown) {{" & ControlChars.CrLf & _
                     "// Get the currently selected layer " & ControlChars.CrLf & _
                     "var layerName = layersDropDown.options[layersDropDown.selectedIndex].value;" & ControlChars.CrLf & _
                     "// Get the fields drop down and the number of items in it" & ControlChars.CrLf & _
                     "var fieldsDropDown = document.getElementById('fieldsDropDown'); " & ControlChars.CrLf & _
                     "var len = fieldsDropDown.options ? fieldsDropDown.options.length : 0;" & ControlChars.CrLf & ControlChars.CrLf & _
                     "// Remove all the drop down's items" & ControlChars.CrLf & _
                     "for(var i=0; i < len; i++)" & ControlChars.CrLf & _
                     "fieldsDropDown.remove(0);" & ControlChars.CrLf & ControlChars.CrLf & _
                     "// Get the fields for the currently selected layer and populate the fields drop down with them" & ControlChars.CrLf & _
                     "var fieldsArray = layerFieldsMapping[layerName];" & ControlChars.CrLf & " _
"if(fieldsArray != null)" & ControlChars.CrLf & _
    "{{ " & ControlChars.CrLf & _
    "for(i=0; i < fieldsArray.length; i++)" & ControlChars.CrLf & _
    "fieldsDropDown.options.add(new Option(fieldsArray[i], fieldsArray[i]));" & ControlChars.CrLf & ControlChars.CrLf & _
    "fieldsDropDown.options[0].selected = true;" & ControlChars.CrLf & _
    "}}" & ControlChars.CrLf & " }}", m_jsonServiceLayersAndFields)
End Get
End Property
  1. Implement another read-only property, HtmlElementIds. This property returns a list of strings, where each string contains the Document Object Model (DOM) Element ID of a control that defines one of the SOE's properties. The IDs returned here are used in saving the extension's properties. Specifically, they are used to retrieve the values that are passed in the Request parameter of the SaveProperties method. In this implementation, the elements defining the extension's properties are the layers' and fields' drop-down lists; therefore, implement HtmlElementIds to return a list containing the IDs of those controls. See the following code example:
[C#]
public List < string > HtmlElementIds
{
    get
    {
        return new List < string > (new string[]
        {
            "layersDropDown", "fieldsDropDown"
        }
        );
    }
}
[VB.NET]
Public ReadOnly Property HtmlElementIds1() As System.Collections.Generic.List(Of String) Implements ESRI.ArcGIS.ServerManager.IServerObjectExtensionConfigurator.HtmlElementIds
Get
Return New List(Of String)(New String() {"layersDropDown", "fieldsDropDown"})
End Get
End Property
  1. The last IServerObjectExtensionConfigurator method to implement is the SaveProperties method. This method's implementation should update the extension's properties according to the information specified on the property page. The method has no return value and defines the following arguments:
    • serverContext—Server context for the service.
    • Request—Values contained by the DOM elements with IDs matching those defined in HtmlElementIds. This ordinarily defines the values to be saved.
    • isEnabled—Whether the extension is enabled on the current server object.
    • ExtensionProperties—SOE properties for the current server object. This must be instantiated in implementations of SaveProperties.
    • InfoProperties—Global (that is, server-level) SOE properties that must be instantiated in implementations of SaveProperties.
    In this case, there are only two properties—layer and field—that need to be saved. These are passed in the Request parameter and can be saved by defining them on the ExtensionProperties parameter. To do this, implement the method as shown in the following code example:
[C#]
public void SaveProperties(ESRI.ArcGIS.Server.IServerContext serverContext,
    System.Collections.Specialized.NameValueCollection Request, bool isEnabled, out
    System.Collections.Specialized.NameValueCollection ExtensionProperties, out
    System.Collections.Specialized.NameValueCollection InfoProperties)
{
    ExtensionProperties = new System.Collections.Specialized.NameValueCollection();
    string layerName = Request["layersDropDown"];
    if (!string.IsNullOrEmpty(layerName))
        ExtensionProperties.Add("LayerName", layerName);
    string fieldName = Request["fieldsDropDown"];
    if (!string.IsNullOrEmpty(fieldName))
        ExtensionProperties.Add("FieldName", fieldName);
    InfoProperties = new System.Collections.Specialized.NameValueCollection();
}
[VB.NET]
Public Sub SaveProperties(ByVal serverContext As ESRI.ArcGIS.Server.IServerContext, ByVal Request As System.Collections.Specialized.NameValueCollection, ByVal isEnabled As Boolean, <System.Runtime.InteropServices.Out()> ByRef ExtensionProperties As System.Collections.Specialized.NameValueCollection, <System.Runtime.InteropServices.Out()> ByRef InfoProperties As System.Collections.Specialized.NameValueCollection) Implements ESRI.ArcGIS.ServerManager.IServerObjectExtensionConfigurator.SaveProperties
    ' Instantiate the properties collection and define the LayerName and FieldName properties.
    ExtensionProperties = New System.Collections.Specialized.NameValueCollection()
    Dim layerName As String = Request("layersDropDown")
    If (Not String.IsNullOrEmpty(layerName)) Then
        ExtensionProperties.Add("LayerName", layerName)
    End If
    Dim fieldName As String = Request("fieldsDropDown")
    If (Not String.IsNullOrEmpty(fieldName)) Then
        ExtensionProperties.Add("FieldName", fieldName)
    End If
    InfoProperties = New System.Collections.Specialized.NameValueCollection()
End Sub


See Also:

Sample: ArcGIS Spatial query server object extension
Walkthrough: Creating a server object extension
How to develop the SOE
How to register the SOE
How to create an SOE client
Sample: Server spatial query COM utility
Developing with ArcGIS Server