ProGuide TableControl - kataya/arcgis-pro-sdk GitHub Wiki

Language:      C#
Subject:       Map Exploration
Contributor:   ArcGIS Pro SDK Team <[email protected]>
Organization:  Esri, http://www.esri.com
Date:          11/24/2020
ArcGIS Pro:    2.7
Visual Studio: 2017, 2019

This ProGuide demonstrates how to use the TableControl to display content from a Catalog view. The code used to illustrate this guide can be found at the Table Control Sample.

Prerequisites

Step 1

Add a new ArcGIS Pro Add-Ins | ArcGIS Pro Dockpane to the add-in project, and name the item TableControlDockpane. Open the config.daml file.

Scroll to the bottom of the file and notice a new button and new dockpane elements have been added. The dockpane follows the standard View-ViewModel pattern used in ArcGIS Pro with the ViewModel defined in the <dockPane className="..." attribute and the View defined in the child <content className="..."

Modify the config.daml file items as follows:

  • Change the button caption to "Show Preview"
  • Change the button tooltip heading to "Show Preview" and the tooltip text to "Show the preview dockpane"
  • Change the dockPane caption to "Preview"
  • Change the dockPane dock property to "bottom" and remove the dockWith property
<controls>
  <!-- add your controls here -->
  <button id="TableControl_TableControlDockpane_ShowButton" caption="Show Preview" 
             className="TableControlDockpane_ShowButton" loadOnClick="true" 
             smallImage="Images\GenericButtonPurple16.png" largeImage="Images\GenericButtonPurple32.png">
    <tooltip heading="Show Preview">Show the preview dockpane<disabledText /></tooltip>
  </button>
</controls>
<dockPanes>
  <dockPane id="TableControl_TableControlDockpane" caption="Preview" 
             className="TableControlDockpaneViewModel" dock="bottom">
    <content className="TableControlDockpaneView" />
  </dockPane>
</dockPanes>

Step 2

Open the TableControlDockpane.xaml file. We will modify this file to add a TableControl to the dockpane. Add a reference to the editing namespace by including the following at the top of the file following the extensions namespace declaration.

xmlns:editing="clr-namespace:ArcGIS.Desktop.Editing;assembly=ArcGIS.Desktop.Editing"

Then replace the entire contents of the <grid> tag with the following

<Grid.RowDefinitions>
  <RowDefinition Height="Auto"/>
  <RowDefinition Height="*"/>
</Grid.RowDefinitions>
<DockPanel LastChildFill="False" Grid.Row="0">
</DockPanel>
<editing:TableControl AutomationProperties.AutomationId="_tableControl" x:Name="tableControl" 
                   Grid.Row="1" VerticalAlignment="Stretch" HorizontalAlignment="Stretch"
                   TableContent="{Binding Path=TableContent}">
</editing:TableControl>

Our dockpane has a DockPanel at the top followed by the TableControl. We will add items to the DockPanel in a later step.

The entire file should look as follows:

<UserControl x:Class="TableControl.TableControlDockpaneView"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008"   
             xmlns:ui="clr-namespace:TableControl"
             xmlns:extensions="clr-namespace:ArcGIS.Desktop.Extensions;assembly=ArcGIS.Desktop.Extensions"
             xmlns:editing="clr-namespace:ArcGIS.Desktop.Editing;assembly=ArcGIS.Desktop.Editing"
             mc:Ignorable="d" 
             d:DesignHeight="300" d:DesignWidth="300"
             d:DataContext="{Binding Path=ui.TableControlDockpaneViewModel}">
    <UserControl.Resources>
      <ResourceDictionary>
          <ResourceDictionary.MergedDictionaries>
              <extensions:DesignOnlyResourceDictionary Source="pack://application:,,,/ArcGIS.Desktop.Framework;component\Themes\Default.xaml"/>
          </ResourceDictionary.MergedDictionaries>
      </ResourceDictionary>
  </UserControl.Resources>
  <Grid>
    <Grid.RowDefinitions>
      <RowDefinition Height="Auto"/>
      <RowDefinition Height="*"/>
    </Grid.RowDefinitions>
    <DockPanel LastChildFill="False" Grid.Row="0">
    </DockPanel>
    <editing:TableControl AutomationProperties.AutomationId="_tableControl" x:Name="tableControl" 
                       Grid.Row="1" VerticalAlignment="Stretch" HorizontalAlignment="Stretch"
                       TableContent="{Binding Path=TableContent}">
    </editing:TableControl>
  </Grid>
</UserControl>

Step 3

The TableControl can be used to display content from layers or tables within a map or from data within the Catalog view. It's content is determined by the TableContent property. This is created using the static TableControlContentFactory class with the Create method. When creating table content it is also recommended to use the IsMapMemberSupported or IsItemSupported functions prior to the Create method to determine if the mapMember or item can be displayed in the TableControl.

Open the TableControlDockpaneViewModel.cs file to begin customization of the ViewModel. Remove the Heading property and the private variable _heading.

Add a new private variable and public property to perform the binding of the table control's content between the View and ViewModel.

    private TableControlContent _tableContent;
    public TableControlContent TableContent
    {
      get => _tableContent;
      set => SetProperty(ref _tableContent, value);
    }

Next we will create and assign content to the table. In this example we will display data from the selected item in the Catalog pane, so will use the ProjectWindowSelectedItemsChangedEvent to determine when a selection occurs within the Catalog window.

In the TableControlDockpaneViewModel constructor subscribe to the ProjectWindowSelectedItemsChangedEvent.

protected TableControlDockpaneViewModel()
{
  ProjectWindowSelectedItemsChangedEvent.Subscribe(OnProjectWindowSelectedItem);
}

Within the OnProjectWindowSelectedItem method we will create the TableControlContent using the TableControlContentFactory.IsItemSupported and TableControlContentFactory.Create methods and then assign the result to the TableContent property.

    private Item _selectedItem;
    private void OnProjectWindowSelectedItem(ProjectWindowSelectedItemsChangedEventArgs args)
    {
      if (args.IProjectWindow.SelectionCount > 0)
      {
        // get the first selected item
        _selectedItem = args.IProjectWindow.SelectedItems.First();

        // check if it's supported by the TableControl
        if (!TableControlContentFactory.IsItemSupported(_selectedItem))
          return;

        // create the content
        var tableContent = TableControlContentFactory.Create(_selectedItem);
        // assign it
        if (tableContent != null)
          this.TableContent = tableContent;
      }
    }

Build the sample and fix any compile errors. Debug the add-in and start ArcGIS Pro. Open a new Map project. Validate the UI by activating the Add-In tab.

showPreview

Click the Show Preview button and see the Preview dockpane appear at the bottom of the ArcGIS Pro window. Navigate to the Catalog pane. Add a new folder connection to the Community Samples Data you previously downloaded by right clicking on Folders and choose Add Folder Connection. Save your project after the connection is made. Navigate to one of the geodatabases in the folder and highlight one of the feature classes. You should see the data populated in the TableControl.

tableControl

Explore the default capabilities of the TableControl including the Row and Column context menus. Explore other datasets in the Community Samples Data. Stop debugging and return to Visual Studio when you are ready to continue.

Step 4

Return to the TableControlDockpane.xaml file. We will add some buttons to the DockPanel to provide additional functionality. Copy the following so that the DockPanel tag looks like the following:

    <DockPanel LastChildFill="False" Grid.Row="0">
      <Button Content="Toggle Sel" Command="editing:TableControlCommands.SwitchSelection"
              CommandTarget="{Binding ElementName=tableControl}"
              Style="{DynamicResource Esri_Button}"/>

      <Button Content="Select All" Command="editing:TableControlCommands.SelectAll"
              CommandTarget="{Binding ElementName=tableControl}"
              Style="{DynamicResource Esri_Button}"/>

      <Button Content="Clear Sel" Command="editing:TableControlCommands.ClearSelection"
              CommandTarget="{Binding ElementName=tableControl}"
              Style="{DynamicResource Esri_Button}"/>

      <Button Content="Test" Command="{Binding AddToMapCommand}"
              Style="{DynamicResource Esri_Button}"/>
    </DockPanel>

There are a predefined set of TableControlCommands which are available for use with the TableControl. They can be referenced in xaml. In the above code snippet you can see we are referencing the SwitchSelection, SelectAll and ClearSelection commands. Take particular note of the CommandTarget property. When referencing these TableControlCommands in xaml it is important to also use the CommandTarget property to reference the TableControl's Name property, otherwise the command does not have the correct context and will not execute.

Step 5

Our final button on the toolbar requires an implementation of the AddToMapCommand. Open the TableControlDockpaneViewModel.cs file and copy the following code.

    private MapMember _selectedMapMember = null;
    private ICommand _addToMapCommand = null;
    public ICommand AddToMapCommand
    {
      get
      {
        if (_addToMapCommand == null)
        {
          _addToMapCommand = new RelayCommand(() =>
          {
            var map = MapView.Active?.Map;
            if (map == null)
              return;

            QueuedTask.Run(() =>
            {
              // test if the selected Catalog item can create a layer
              if (LayerFactory.Instance.CanCreateLayerFrom(_selectedItem))
                _selectedMapMember = LayerFactory.Instance.CreateLayer(_selectedItem, map);

              // test if the selected Catalog item can create a table
              else if (StandaloneTableFactory.Instance.CanCreateStandaloneTableFrom(_selectedItem))
                _selectedMapMember = StandaloneTableFactory.Instance.CreateStandaloneTable(_selectedItem, map);

              else
                _selectedMapMember = null;
            });

          });
        }
        return _addToMapCommand;
      }
    }

This command uses the LayerFactory and StandaloneTableFactory static classes to check the selected Catalog item before adding it to the active map.

Debug the add-in and start ArcGIS Pro. Open your previous project. Return to the Catalog window and your data connection folder to populate the table with content.

TableControlButtons

Now test the buttons added to the toolbar. Note that because the table control has been loaded from the Catalog item, any table selection will be independent of the map selection even if you add the loaded data to the map.

When finished, close Pro and return to Visual Studio.

Step 6

Our final step is to customize the row context menu. The default row context menu for the TableControl provides one menu item to toggle the selection of the active row. We will also add the ability to zoom to the active row in the map.

First, define a resource for the context menu in the xaml file. Open TableControlDockpane.xaml and add the following so the entire UserControl.Resources at the top of the file looks like this:

    <UserControl.Resources>
      <ResourceDictionary>
          <ResourceDictionary.MergedDictionaries>
              <extensions:DesignOnlyResourceDictionary Source="pack://application:,,,/ArcGIS.Desktop.Framework;component\Themes\Default.xaml"/>
          </ResourceDictionary.MergedDictionaries>

        <ContextMenu x:Key="MyRowContextMenu">
          <MenuItem Header="Select/Unselect" 
                      Command="editing:TableControlCommands.ToggleRowSelection">
          </MenuItem>
        </ContextMenu>
    </ResourceDictionary>
  </UserControl.Resources>

A new ContextMenu resource called MyRowContextMenu now exists. It contains a single menu item that executes the ToggleRowSelection TableControl command. The second command, ZoomToRow will be added in code.

Bind the RowContextMenu and SelectedRowContextMenu properties in the TableControl declaration to the MyRowContextMenu resource as follows:

    <editing:TableControl AutomationProperties.AutomationId="_tableControl" x:Name="tableControl" 
                       Grid.Row="1" VerticalAlignment="Stretch" HorizontalAlignment="Stretch"
                       TableContent="{Binding Path=TableContent}" 
                       RowContextMenu="{StaticResource MyRowContextMenu}"
                       SelectedRowContextMenu="{StaticResource MyRowContextMenu}">
    </editing:TableControl>

The RowContextMenu property is the context menu for rows when in All Record view mode. The SelectedRowContextMenu is the context menu for rows when in Selected Record view mode.

Next open the TableControlDockpane.xaml.cs file. In the TableControlDockpaneView constructor, add a hook to the Loaded event. We will use this event to pass a reference to the TableControl object to our ViewModel.

public TableControlDockpaneView()
{
  InitializeComponent();
  Loaded += TableControlDockpaneView_Loaded;
}

private void TableControlDockpaneView_Loaded(object sender, RoutedEventArgs e)
{
  var dockpane = this.DataContext as TableControlDockpaneViewModel;
  dockpane.SetTable(this.tableControl);
}

In the TableControlDockpaneViewModel.cs file, add the following methods which appends our custom 'Zoom to Row' menu item to the context menus when the TableControl is assigned.

    private ArcGIS.Desktop.Editing.TableControl _tableControl = null;
    internal void SetTable(ArcGIS.Desktop.Editing.TableControl tableControl)
    {
      _tableControl = tableControl;
      UpdateContextMenu();
    }

    private bool _contextmenu_item_added = false;
    private void UpdateContextMenu()
    {
      if (_contextmenu_item_added)
        return;

      if (_tableControl.RowContextMenu == null)
        return;
      var mnuItem_Zoom = new System.Windows.Controls.MenuItem() { Header = "Zoom to Row", Command = this.ZoomToRowCommand };
      _tableControl.RowContextMenu.Items.Add(mnuItem_Zoom);

      _contextmenu_item_added = true;
    }

Finally add the ZoomToRowCommand method.

    private ICommand _zoomToRowCommand = null;
    public ICommand ZoomToRowCommand
    {
      get
      {
        if (_zoomToRowCommand == null)
        {
          _zoomToRowCommand = new RelayCommand(() =>
          {
            // if we have some content, a map and our data is added to the map
            if (_tableControl.TableContent != null && MapView.Active != null && _selectedMapMember != null)
            {
              // get the oid of the active row
              var oid = _tableControl.GetObjectIdAsync(_tableControl.ActiveRowIndex).Result;
              // load into an inspector to obtain the Shape
              var insp = new Inspector();
              insp.LoadAsync(_selectedMapMember, oid).ContinueWith((t) =>
              {
                // zoom
                MapView.Active.ZoomToAsync(insp.Shape.Extent, new TimeSpan(0, 0, 0, 1));
              });
            }
          });
        }
        return _zoomToRowCommand;
      }
    }

Use the GetObjectIdAsync method passing the ActiveRowIndex to retrieve the objectID of the active row. Load an Inspector object with this feature to obtain the Shape. Use the ZoomToAsync method on the active mapView to zoom to that extent.

Build the sample and fix any compile errors. Debug the add-in and start ArcGIS Pro. Open the previous project. Load data from the Community Samples Data. Click the 'Add to Map' button for your data to be added to the active map. When you right click on a row in the TableControl you should see the custom context menu. Choose the 'Zoom to Row' option and the map will zoom to that record.

TableControlContextMenu

Stop debugging and return to Visual Studio.

⚠️ **GitHub.com Fallback** ⚠️