This tutorial explains how you can customize an existing handles provider to:

  • Change the default settings of a handles provider

  • Customize the handles used by a handles provider

  • Make sure that the customized handles provider is used during editing

Other customizations

You need to customize a handles provider for advanced use cases only. For other customization methods, see this article.

The sample_editing sample supports this tutorial. It’s available for C++ and C#.

See this article for more information about editing.

Configuring a handles provider

You can change the configuration of most handles provider implementations. For example, you can change the minimum and maximum point counts for the PolylineHandlesProvider and PolylineRingHandlesProvider, or you can change the handles they use.

The following sample code shows you how to create and customize a handles provider.

Program (C++): Creating a handles provider
auto handlesProvider = std::make_shared<PolylineHandlesProvider>();
handlesProvider->setMaxPointCount(8);

C#

var handlesProvider = new PolylineHandlesProvider {MaxPointCount = 8};

After creating a handles provider, you must configure it so that it’s used during editing:

  1. Make sure that an IFeatureHandlesProvider uses the geometry handles provider.

    Program (C++): Let the feature handles provider use the custom geometry handles provider
    // Register the custom handles provider in a default composite with a high priority, so that it is used before all other handles providers
    // Using a composite instead of directly using the custom PolylineHandlesProvider makes sure that other geometries remain supported.
    auto geometryHandlesProvider = CompositeGeometryHandlesProvider::createDefault();
    geometryHandlesProvider->add(handlesProvider, Priority::High);
    
    // Make sure the custom geometry handles provider is used to editing the features
    auto featureHandlesProvider = std::make_shared<FeatureHandlesProvider>();
    featureHandlesProvider->setGeometryHandlesProvider(geometryHandlesProvider);

    C#

    // Register the custom handles provider in a default composite with a high priority, so that it is used before all other handles providers
    // Using a composite instead of directly using the custom PolylineHandlesProvider makes sure that other geometries remain supported.
    var geometryHandlesProvider = CompositeGeometryHandlesProvider.CreateDefault();
    geometryHandlesProvider.Add(handlesProvider, Priority.High);
    
    // Make sure the custom geometry handles provider is used to editing the features
    var featureHandlesProvider = new FeatureHandlesProvider {GeometryHandlesProvider = geometryHandlesProvider};
  2. Configure that IFeatureHandlesProvider in an IFeatureEditConfiguration

    Program (C++): Configure the feature handles provider in a feature edit configuration
    class CustomEditConfiguration final : public IFeatureEditConfiguration {
    public:
      void edit(const Feature& /*feature*/, LayerId /*layerId*/, const std::shared_ptr<Map>& /*map*/, FeatureEditConfigurationBuilder& builder) const override {
        // Use a custom handles provider for all features
        builder.handlesProvider(_featureHandlesProvider).submit();
      }
    
    private:
      std::shared_ptr<IFeatureHandlesProvider> _featureHandlesProvider;
    };

    C#

    private sealed class CustomEditConfiguration : IFeatureEditConfiguration
    {
        private readonly IFeatureHandlesProvider _featureHandlesProvider;
    
        public void Edit(Feature feature, ulong layerId, Map map, FeatureEditConfigurationBuilder builder)
        {
            // Use a custom handles provider for all features
            builder.HandlesProvider(_featureHandlesProvider).Submit();
        }
    }
  3. Configure that IFeatureEditConfiguration on the FeatureLayer.

    Program (C++): Configuring the feature edit configuration on the layer
    // Use a custom edit configuration that changes the editing behavior for this layer
    auto customEditConfiguration = std::make_shared<CustomEditConfiguration>();
    
    // Register the configuration. This makes the layer editable by default
    return FeatureLayer::newBuilder()
        .model(model) //
        .title("Tutorial 1")
        .editConfiguration(customEditConfiguration)
        .build();

    C#

    // Use a custom edit configuration that changes the editing behavior for this layer
    var customEditConfiguration = new CustomEditConfiguration();
    
    // Register the configuration. This makes the layer editable by default
    return FeatureLayer.NewBuilder()
        .Model(model)
        .Title("Tutorial 1")
        .EditConfiguration(customEditConfiguration)
        .Build();

Changing the handles of a handle provider

For more fine-grained control over the handles used by a handles provider, you can implement your own handle factory. For example, if you implement your own handle factory for polylines, you can decide which handles it creates and uses to:

  • Move points

  • Remove points

  • Insert points

  • Change the elevation of a point

You can customize the handle factories for other geometries in a similar way. The handle to translate a Feature is provided by an IFeatureHandlesProvider. You can use the handle factory for the default implementation, FeatureHandlesProvider to customize the translate handle.

The following sections show you how to implement and use a handle factory.

Using a handle factory wrapper

You can configure custom handle factory implementations, typically wrappers, on the corresponding handles provider.

Program (C++): Configuring custom handle factory implementations
// A custom handle factory provides fine-grained control over the handles that are used by the handles provider
auto defaultHandleFactory = handlesProvider->getHandleFactory();
auto customHandleFactory = std::make_shared<CustomPolylineHandleFactory>(defaultHandleFactory);
handlesProvider->setHandleFactory(customHandleFactory);

C#

// A custom handle factory provides fine-grained control over the handles that are used by the handles provider
var defaultHandleFactory = handlesProvider.HandleFactory;
var customHandleFactory = new CustomPolylineHandleFactory(defaultHandleFactory);
handlesProvider.HandleFactory = customHandleFactory;

Omitting a handle

To omit a type of handle, the corresponding factory method must return null.

Program (C++): Omitting a handle type
std::shared_ptr<IEditHandle> CustomPolylineHandleFactory::createInsertPointHandle(const std::shared_ptr<ObservablePolyline>& /*polyline*/,
                                                                                  size_t /*insertIndex*/,
                                                                                  const std::shared_ptr<IPointEditAction>& /*editAction*/,
                                                                                  const std::shared_ptr<FeatureEditContext>& /*context*/) {
  // Make sure that PolylineHandlesProvider doesn't create insert handles anymore
  // Note that it will still create append and prepend handles though.
  return nullptr;
}

C#

public override IEditHandle CreateInsertPointHandle(ObservablePolyline polyline, uint insertIndex, IPointEditAction action, FeatureEditContext context)
{
    // Make sure that PolylineHandlesProvider doesn't create insert handles anymore
    // Note that it will still create append and prepend handles though.
    return null;
}

Replacing a handle

To replace a handle, you must re-implement the corresponding factory method. In the following example, we replace the handle to remove points from a polyline.

Program (C++): Replacing a handle
std::shared_ptr<IEditHandle> CustomPolylineHandleFactory::createRemovePointHandle(const std::shared_ptr<ObservablePolyline>& polyline,
                                                                                  size_t pointIndex,
                                                                                  const std::shared_ptr<IPointEditAction>& editAction,
                                                                                  const std::shared_ptr<FeatureEditContext>& /*context*/) {
  // Derive an observable Point from the observable Polyline. This point will be updated whenever the polyline is changed
  auto observableLocation = ObservablePolyline::derivePointAtIndex(polyline, pointIndex);

  // Create a handle that is initially positioned on this location
  auto handle = std::make_shared<PointEditHandle>(observableLocation);

  // The handle will call the edit action when clicking on it using the right mouse button
  handle->addOnClickAction(editAction, 1).mouseButton(MouseButton::Right).cursor(MouseCursor::Arrow);

  // Use a small red icon
  handle->setRegularIcon(std::make_shared<BasicIcon>(IconType::FilledRectangle, 6, Color(255, 0, 0, 64)));
  handle->setHighlightedIcon(std::make_shared<BasicIcon>(IconType::FilledRectangle, 6, Color(255, 0, 0, 192)));
  return handle;
}

C#

public override IEditHandle CreateRemovePointHandle(ObservablePolyline polyline, uint pointIndex, IPointEditAction action, FeatureEditContext context)
{
    // Derive an observable Point from the observable Polyline. This point will be updated whenever the polyline is changed
    var observableLocation = ObservablePolyline.DerivePointAtIndex(polyline, pointIndex);

    // Create a handle that is initially positioned on this location
    var handle = new PointEditHandle(observableLocation);

    // The handle will call the edit action when clicking on it using the right mouse button
    handle.AddOnClickAction(action, 1).MouseButton(MouseButton.Right).Cursor(MouseCursor.Arrow);

    // Use a small red icon
    handle.RegularIcon = new BasicIcon(BasicIcon.IconType.FilledRectangle, 6, Color.FromArgb(64, 255, 0, 0));
    handle.HighlightedIcon = new BasicIcon(BasicIcon.IconType.FilledRectangle, 6, Color.FromArgb(192, 255, 0, 0));
    return handle;
}

Adding a handle

You can’t use the handle factories to add an extra handle. It requires a different approach. For a demonstration, see this tutorial.