Widget with Feature Actions

This Operations Dashboard for ArcGIS sample demonstrates how to use built-in and custom feature actions on a custom widget. The feature actions are configurable in the widget's configuration dialog by implementing the FeatureActionList. The configured feature actions are serialized when the operation view is saved. The FeatureActionContextMenu is used to show the configured feature actions in a context menu when right-clicking on a feature in the list of features the widget is showing.
Operations Dashboard for ArcGIS samples are supported only in Visual Studio 2012. A live preview is not available.
To run this sample, open the solution in Visual Studio 2012, set the Start Action and Start Options debug options in the Project Properties as described in the Testing add-ins help topic, and then build and run the project.
Download Sample Application<UserControl x:Class="WidgetWithFeatureActions.WidgetWithFeatureActions" 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:opsDash="clr-namespace:ESRI.ArcGIS.OperationsDashboard;assembly=ESRI.ArcGIS.OperationsDashboard" xmlns:opsDashCtl="clr-namespace:ESRI.ArcGIS.OperationsDashboard.Controls;assembly=ESRI.ArcGIS.OperationsDashboard" mc:Ignorable="d" d:DesignHeight="300" d:DesignWidth="300"> <Grid Margin="10"> <ListBox x:Name="FeatureListBox" Background="Transparent" Style="{StaticResource ThemedListBoxStyle}"> <ListBox.ItemTemplate> <DataTemplate> <ContentControl Tag="{Binding DataContext, ElementName=FeatureListBox}"> <TextBlock Text="{Binding FieldValue}" Style="{DynamicResource LargeTextBlockStyle}" /> <ContentControl.ContextMenu> <opsDashCtl:FeatureActionContextMenu FeatureActions="{Binding PlacementTarget.Tag.FeatureActions, RelativeSource={RelativeSource Self}, Mode=OneWay}" DataSource="{Binding PlacementTarget.Tag.DataSource, RelativeSource={RelativeSource Self}, Mode=OneWay}" Feature="{Binding Graphic, Mode=OneWay}" /> </ContentControl.ContextMenu> </ContentControl> </DataTemplate> </ListBox.ItemTemplate> </ListBox> </Grid> </UserControl>
// Copyright 2013 ESRI // // All rights reserved under the copyright laws of the United States // and applicable international laws, treaties, and conventions. // // You may freely redistribute and use this sample code, with or // without modification, provided you include the original copyright // notice and use restrictions. // // See the use restrictions http://help.arcgis.com/en/sdk/10.0/usageRestrictions.htm. // using ESRI.ArcGIS.OperationsDashboard; using System; using System.Collections.Generic; using System.ComponentModel.Composition; using System.Linq; using System.Runtime.Serialization; using System.Windows; using System.Windows.Controls; using client = ESRI.ArcGIS.Client; namespace WidgetWithFeatureActions { /// <summary> /// Defines the types of feature actions used by this widget. /// </summary> public enum FeatureActionType { Highlight, Follow, ShowFeatureAttributes } /// <summary> /// A Widget is a dockable extension to ArcGIS Operations Dashboard application that implements IWidget. By returning true from CanConfigure, /// this widget provides the ability for the user to configure the widget properties showing a settings Window in the Configure method. /// By implementing IDataSourceConsumer, tthis Widget indicates it requires a DataSource to function and will be notified when the /// data source is updated or removed. /// By implementing INotifyPropertyChanged, any changes made to the widget Caption by the user during configuration is reflected immediately /// in the title bar of the widget within the application. /// </summary> [Export("ESRI.ArcGIS.OperationsDashboard.Widget")] [ExportMetadata("DisplayName", "Feature List")] [ExportMetadata("Description", "Lists features from a data source based on a configured field name.")] [ExportMetadata("ImagePath", "/WidgetWithFeatureActions;component/Images/Widget32.png")] [ExportMetadata("DataSourceRequired", true)] [DataContract] public partial class WidgetWithFeatureActions : UserControl, IWidget, IDataSourceConsumer { public WidgetWithFeatureActions() { InitializeComponent(); //set the DataContext of FeatureListBox to allow data binding // FeatureListBox.DataContext = this; } /// <summary> /// Gets the configured data source. /// </summary> public DataSource DataSource { get { return OperationsDashboard.Instance.DataSources.FirstOrDefault((dataSource) => dataSource.Id == DataSourceIds[0]); } } /// <summary> /// Gets/sets the feature actions shown in the FeatureActionContextMenu. /// </summary> public IEnumerable<IFeatureAction> FeatureActions { get; set; } [DataMember(Name = "featureActions")] public FeatureActionType[] PersistedFeatureActions { get; set; } /// <summary> /// Gets/sets the state of the UpdateExtentType of the HighlighFeatureAction /// when configured. /// </summary> [DataMember(Name = "highlightUpdateType")] public UpdateExtentType HighlightUpdateType { get; set; } /// <summary> /// The name of a field within the selected data source. This property is set during widget configuration. /// </summary> [DataMember(Name = "field")] public string Field { get; set; } #region IWidget Members private string _caption = "Default Caption"; /// <summary> /// The text that is displayed in the widget's containing window title bar. This property is set during widget configuration. /// </summary> [DataMember(Name = "caption")] public string Caption { get { return _caption; } set { if (value != _caption) { _caption = value; } } } /// <summary> /// The unique identifier of the widget, set by the application when the widget is added to the configuration. /// </summary> [DataMember(Name = "id")] public string Id { get; set; } /// <summary> /// OnActivated is called when the widget is first added to the configuration, or when loading from a saved configuration, after all /// widgets have been restored. Saved properties can be retrieved, including properties from other widgets. /// Note that some widgets may have properties which are set asynchronously and are not yet available. /// </summary> public void OnActivated() { InitializeFeatureActions(); } /// <summary> /// OnDeactivated is called before the widget is removed from the configuration. /// </summary> public void OnDeactivated() { } /// <summary> /// Determines if the Configure method is called after the widget is created, before it is added to the configuration. Provides an opportunity to gather user-defined settings. /// </summary> /// <value>Return true if the Configure method should be called, otherwise return false.</value> public bool CanConfigure { get { return true; } } /// <summary> /// Provides functionality for the widget to be configured by the end user through a dialog. /// </summary> /// <param name="owner">The application window which should be the owner of the dialog.</param> /// <param name="dataSources">The complete list of DataSources in the configuration.</param> /// <returns>True if the user clicks ok, otherwise false.</returns> public bool Configure(Window owner, IList<DataSource> dataSources) { // Show the configuration dialog. Config.WidgetWithFeatureActionsDialog dialog = new Config.WidgetWithFeatureActionsDialog(dataSources, Caption, DataSourceIds != null ? DataSourceIds[0] : null, Field, FeatureActions) { Owner = owner }; if (dialog.ShowDialog() != true) return false; // Retrieve the selected values for the properties from the configuration dialog. Caption = dialog.Caption; DataSourceIds = new string[] { dialog.DataSource.Id }; Field = dialog.Field.Name; FeatureActions = dialog.SelectedFeatureActions; InitializePersistedFeatureActions(); return true; } #endregion #region IDataSourceConsumer Members /// <summary> /// Returns the ID(s) of the data source(s) consumed by the widget. /// </summary> [DataMember(Name = "dataSourceIds")] public string[] DataSourceIds { get; set; } /// <summary> /// Called when a DataSource is removed from the configuration. /// </summary> /// <param name="dataSource">The DataSource being removed.</param> public void OnRemove(DataSource dataSource) { // Respond to data source being removed. DataSourceIds = null; } /// <summary> /// Called when a DataSource found in the DataSourceIds property is updated. /// </summary> /// <param name="dataSource">The DataSource being updated.</param> public async void OnRefresh(DataSource dataSource) { var result = await dataSource.ExecuteQueryAsync(new Query()); if (result == null || result.Features == null) return; FeatureListBox.ItemsSource = result.Features.Select((graphic) => { return new Feature(graphic, Field); }); } #endregion /// <summary> /// Initializes the feature actions used by the widget. /// </summary> private void InitializeFeatureActions() { FeatureActions = null; if (PersistedFeatureActions == null) return; List<IFeatureAction> featureActions = new List<IFeatureAction>(); foreach (var persistedFeatureAction in PersistedFeatureActions) { switch (persistedFeatureAction) { case FeatureActionType.Highlight: featureActions.Add(new HighlightFeatureAction() { UpdateExtent = HighlightUpdateType }); break; case FeatureActionType.Follow: featureActions.Add(new FollowFeatureAction()); break; case FeatureActionType.ShowFeatureAttributes: featureActions.Add(new ShowAttributesFeatureAction()); break; default: throw new NotImplementedException(string.Format("Cannot create feature action of type: {0}", persistedFeatureAction.ToString())); } } FeatureActions = featureActions; } /// <summary> /// Initializes the PersistedFeatureActions property used to persist the feature actions /// selected in the config dialog. /// </summary> private void InitializePersistedFeatureActions() { PersistedFeatureActions = null; HighlightUpdateType = UpdateExtentType.Pan; if (FeatureActions == null) return; List<FeatureActionType> persistedFeatureActions = new List<FeatureActionType>(); foreach (var featureAction in FeatureActions) { if (featureAction is HighlightFeatureAction) { persistedFeatureActions.Add(FeatureActionType.Highlight); //persist the UpdateExtent state of the highlight feature action HighlightUpdateType = ((HighlightFeatureAction)featureAction).UpdateExtent; } else if (featureAction is FollowFeatureAction) persistedFeatureActions.Add(FeatureActionType.Follow); else if (featureAction is ShowAttributesFeatureAction) persistedFeatureActions.Add(FeatureActionType.ShowFeatureAttributes); else throw new NotImplementedException(string.Format("Cannot persist feature action of type: {0}", featureAction.GetType().ToString())); } PersistedFeatureActions = persistedFeatureActions.ToArray(); } } /// <summary> /// Helper class that represents a feature in the collection of features /// bound to the FeatureListBox. /// </summary> public class Feature { readonly string _field; public Feature(client.Graphic feature, string field) { Graphic = feature; _field = field; } public client.Graphic Graphic { get; private set; } public string FieldValue { get { return Graphic.Attributes[_field].ToString(); } } } }
' Copyright 2013 ESRI ' ' All rights reserved under the copyright laws of the United States ' and applicable international laws, treaties, and conventions. ' ' You may freely redistribute and use this sample code, with or ' without modification, provided you include the original copyright ' notice and use restrictions. ' ' See the use restrictions http://help.arcgis.com/en/sdk/10.0/usageRestrictions.htm. ' Imports System Imports System.Collections.Generic Imports System.Linq Imports System.Text Imports System.Threading Imports System.Threading.Tasks Imports System.Windows Imports System.Windows.Controls Imports System.ComponentModel Imports System.ComponentModel.Composition Imports System.Runtime.CompilerServices Imports System.Runtime.Serialization Imports ESRI.ArcGIS.OperationsDashboard Imports client = ESRI.ArcGIS.Client ''' <summary> ''' Defines the types of feature actions used by this widget. ''' </summary> Public Enum FeatureActionType Highlight Follow ShowFeatureAttributes End Enum ''' <summary> ''' A Widget is a dockable extension to ArcGIS Operations Dashboard application that implements IWidget. By returning true from CanConfigure, ''' this widget provides the ability for the user to configure the widget properties showing a settings Window in the Configure method. ''' By implementing IDataSourceConsumer, this widget indicates it requires a DataSource to function and will be notified when the ''' data source is updated or removed. ''' By implementing INotifyPropertyChanged, any changes made to the widget Caption by the user during configuration is reflected immediately ''' in the title bar of the widget within the application. ''' </summary> <Export("ESRI.ArcGIS.OperationsDashboard.Widget"), ExportMetadata("DisplayName", "Feature List"), ExportMetadata("Description", "Lists features from a data source based on a configured field name."), ExportMetadata("ImagePath", "/WidgetWithFeatureActions;component/Images/Widget32.png"), ExportMetadata("DataSourceRequired", True), DataContract()> Public Class WidgetWithFeatureActions Inherits UserControl Implements IWidget, IDataSourceConsumer ''' <summary> ''' Gets the configured data source. ''' </summary> Public ReadOnly Property DataSource() As DataSource Get Return OperationsDashboard.Instance.DataSources.FirstOrDefault(Function(dataSource__1) dataSource__1.Id = DataSourceIds(0)) End Get End Property ''' <summary> ''' Gets/sets the feature actions shown in the FeatureActionContextMenu. ''' </summary> Public Property FeatureActions() As IEnumerable(Of IFeatureAction) Get Return m_FeatureActions End Get Set(value As IEnumerable(Of IFeatureAction)) m_FeatureActions = value End Set End Property Private m_FeatureActions As IEnumerable(Of IFeatureAction) ''' <summary> ''' A unique identifier of a data source in the configuration. This property is set during widget configuration. ''' </summary> <DataMember(Name:="dataSourceId")> _ Public Property DataSourceId() As String Get Return m_DataSourceId End Get Set(value As String) m_DataSourceId = value End Set End Property Private m_DataSourceId As String <DataMember(Name:="featureActions")> _ Public Property PersistedFeatureActions() As FeatureActionType() Get Return m_PersistedFeatureActions End Get Set(value As FeatureActionType()) m_PersistedFeatureActions = value End Set End Property Private m_PersistedFeatureActions As FeatureActionType() ''' <summary> ''' Gets/sets the state of the UpdateExtentType of the HighlighFeatureAction ''' when configured. ''' </summary> <DataMember(Name:="highlightUpdateType")> _ Public Property HighlightUpdateType() As UpdateExtentType Get Return m_HighlightUpdateType End Get Set(value As UpdateExtentType) m_HighlightUpdateType = value End Set End Property Private m_HighlightUpdateType As UpdateExtentType ''' <summary> ''' The name of a field within the selected data source. This property is set during widget configuration. ''' </summary> <DataMember(Name:="field")> Public Property Field As String Private _caption As String = "Default Caption" ''' <summary> ''' The text that is displayed in the widget's containing window title bar. This property is set during widget configuration. ''' </summary> <DataMember(Name:="caption")> Public Property Caption As String Implements IWidget.Caption Get Return _caption End Get Set(value As String) _caption = value End Set End Property ''' <summary> ''' The unique identifier of the widget, set by the application when the widget is added to the configuration. ''' </summary> <DataMember(Name:="id")> Public Property Id As String Implements IWidget.Id ''' <summary> ''' OnActivated is called when the widget is first added to the configuration, or when loading from a saved configuration, after all ''' widgets have been restored. Saved properties can be retrieved, including properties from other widgets. ''' Note that some widgets may have properties which are set asynchronously and are not yet available. ''' </summary> ''' <remarks></remarks> Public Sub OnActivated() Implements IWidget.OnActivated InitializeFeatureActions() End Sub ''' <summary> ''' OnDeactivated is called before the widget is removed from the configuration. ''' </summary> Public Sub OnDeactivated() Implements IWidget.OnDeactivated End Sub ''' <summary> ''' Determines if the Configure method is called after the widget is created, before it is added to the configuration. Provides an opportunity to gather user-defined settings. ''' </summary> ''' <value>Return true if the Configure method should be called, otherwise return false.</value> Public ReadOnly Property CanConfigure As Boolean Implements IWidget.CanConfigure Get Return True End Get End Property ''' <summary> ''' Provides functionality for the widget to be configured by the end user through a dialog. ''' </summary> ''' <param name="owner">The application window which should be the owner of the dialog.</param> ''' <param name="dataSources">The complete list of DataSources in the configuration.</param> ''' <returns>True if the user clicks ok, otherwise false.</returns> Public Function Configure(owner As Window, dataSources As IList(Of ESRI.ArcGIS.OperationsDashboard.DataSource)) As Boolean Implements IWidget.Configure ' Show the configuration dialog. Dim dialog As New Config.WidgetWithFeatureActionsDialog(dataSources, Caption, DataSourceId, Field, FeatureActions) With { _ .Owner = owner _ } If dialog.ShowDialog() <> True Then Return False End If ' Retrieve the selected values for the properties from the configuration dialog. Caption = dialog.Caption DataSourceId = dialog.DataSource.Id Field = dialog.Field.Name FeatureActions = dialog.SelectedFeatureActions InitializePersistedFeatureActions() Return True End Function ''' <summary> ''' Returns the ID(s) of the data source(s) consumed by the widget. ''' </summary> Public ReadOnly Property DataSourceIds() As String() Implements IDataSourceConsumer.DataSourceIds Get Return New String() {DataSourceId} End Get End Property ''' <summary> ''' Called when a DataSource is removed from the configuration. ''' </summary> ''' <param name="dataSource">The DataSource being removed.</param> Public Sub OnRemove(dataSource As ESRI.ArcGIS.OperationsDashboard.DataSource) Implements IDataSourceConsumer.OnRemove ' Respond to data source being removed. DataSourceId = Nothing End Sub ''' <summary> ''' Called when a DataSource found in the DataSourceIds property is updated. ''' </summary> ''' <param name="dataSource">The DataSource being updated.</param> Public Async Sub OnRefresh(dataSource As ESRI.ArcGIS.OperationsDashboard.DataSource) Implements IDataSourceConsumer.OnRefresh Dim result = Await dataSource.ExecuteQueryAsync(New Query()) If result Is Nothing OrElse result.Features Is Nothing Then Return End If FeatureListBox.ItemsSource = result.Features.[Select](Function(graphic) Return New Feature(graphic, Field) End Function) End Sub ''' <summary> ''' Initializes the feature actions used by the widget. ''' </summary> Private Sub InitializeFeatureActions() FeatureActions = Nothing If PersistedFeatureActions Is Nothing Then Return End If Dim featureActions__1 As New List(Of IFeatureAction)() For Each persistedFeatureAction As FeatureActionType In PersistedFeatureActions Select Case persistedFeatureAction Case FeatureActionType.Highlight featureActions__1.Add(New HighlightFeatureAction() With { _ .UpdateExtent = HighlightUpdateType _ }) Exit Select Case FeatureActionType.Follow featureActions__1.Add(New FollowFeatureAction()) Exit Select Case FeatureActionType.ShowFeatureAttributes featureActions__1.Add(New ShowAttributesFeatureAction()) Exit Select Case Else Throw New NotImplementedException(String.Format("Cannot create feature action of type: {0}", persistedFeatureAction.ToString())) End Select Next FeatureActions = featureActions__1 End Sub ''' <summary> ''' Initializes the PersistedFeatureActions property used to persist the feature actions ''' selected in the config dialog. ''' </summary> Private Sub InitializePersistedFeatureActions() PersistedFeatureActions = Nothing HighlightUpdateType = UpdateExtentType.Pan If FeatureActions Is Nothing Then Return End If Dim persistedFeatureActions__1 As New List(Of FeatureActionType)() For Each featureAction As IFeatureAction In FeatureActions If TypeOf featureAction Is HighlightFeatureAction Then persistedFeatureActions__1.Add(FeatureActionType.Highlight) 'persist the UpdateExtent state of the highlight feature action HighlightUpdateType = DirectCast(featureAction, HighlightFeatureAction).UpdateExtent ElseIf TypeOf featureAction Is FollowFeatureAction Then persistedFeatureActions__1.Add(FeatureActionType.Follow) ElseIf TypeOf featureAction Is ShowAttributesFeatureAction Then persistedFeatureActions__1.Add(FeatureActionType.ShowFeatureAttributes) Else Throw New NotImplementedException(String.Format("Cannot persist feature action of type: {0}", featureAction.[GetType]().ToString())) End If Next PersistedFeatureActions = persistedFeatureActions__1.ToArray() End Sub Public Sub New() ' This call is required by the designer. InitializeComponent() 'set the DataContext of FeatureListBox to allow data binding ' FeatureListBox.DataContext = Me End Sub End Class ''' <summary> ''' Helper class that represents a feature in the collection of features ''' bound to the FeatureListBox. ''' </summary> Public Class Feature ReadOnly _field As String Public Sub New(feature__1 As client.Graphic, field As String) Graphic = feature__1 _field = field End Sub Public Property Graphic() As client.Graphic Get Return m_Graphic End Get Private Set(value As client.Graphic) m_Graphic = value End Set End Property Private m_Graphic As client.Graphic Public ReadOnly Property FieldValue() As String Get Return Graphic.Attributes(_field).ToString() End Get End Property End Class