Count Widget
This Operations Dashboard for ArcGIS sample demonstrates how to implement a custom widget that executes a statistical query against a data source and shows the number of features that match the query.
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="CountWidgetCS.CountWidget" 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:opsCenter="clr-namespace:ESRI.ArcGIS.OperationsCenter;assembly=ESRI.ArcGIS.OperationsCenter" mc:Ignorable="d" d:DesignHeight="300" d:DesignWidth="300"> <Grid Margin="10"> <Grid.RowDefinitions> <RowDefinition Height="auto" /> <RowDefinition Height="auto" /> <RowDefinition Height="*" /> </Grid.RowDefinitions> <Grid.ColumnDefinitions> <ColumnDefinition Width="*" /> </Grid.ColumnDefinitions> <TextBlock Grid.Row="0" Text="Number of features where" HorizontalAlignment="Left" Margin="10" Style="{StaticResource SettingLabelStyle}" Foreground="{DynamicResource ThemedForegroundBrush}" FontSize="{DynamicResource ThemedTextSize}" /> <TextBlock x:Name="CountOfLabel" Grid.Row="1" Text="" HorizontalAlignment="Left" Margin="10" Style="{StaticResource SettingLabelStyle}" Foreground="{DynamicResource ThemedForegroundBrush}" FontSize="{DynamicResource ThemedTextSize}" /> <Viewbox Grid.Row="2" Stretch="Uniform"> <TextBlock x:Name="CountBlock" Text="No Data" Margin="10" Foreground="{DynamicResource ThemedForegroundBrush}"/> </Viewbox> </Grid> </UserControl>
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using System.ComponentModel; using System.ComponentModel.Composition; using System.Runtime.CompilerServices; using System.Runtime.Serialization; using ESRI.ArcGIS.OperationsCenter; using client = ESRI.ArcGIS.Client; namespace CountWidgetCS { /// <summary> /// A Widget is a dockable extension to the ArcGIS Operations Center 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.OperationsCenter.Widget")] [ExportMetadata("DisplayName", "CountWidget SDK Sample C#")] [ExportMetadata("Description", "WPF Runtime SDK sample widget that counts features containing a specific attribute value.")] [ExportMetadata("ImagePath", "/CountWidgetCS;component/Images/Calculator.png")] [ExportMetadata("DataSourceRequired", true)] [DataContract] public partial class CountWidget : UserControl, IWidget, IDataSourceConsumer { /// <summary> /// A unique identifier of a data source in the configuration. This property is set during widget configuration. /// </summary> [DataMember(Name = "dataSourceId")] public string DataSourceId { 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 FieldName { get; set; } private client.Field Field { get; set; } [DataMember(Name = "value")] public string Value { get; set; } private string QueryValue { get; set; } public CountWidget() { InitializeComponent(); Value = "0"; Caption = "New Count Widget"; } private void UpdateControls() { CountOfLabel.Text = string.Format("{0}={1}", FieldName, Value); } #region IWidget Members private string _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> /// Activate 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 Activate() { UpdateControls(); } /// <summary> /// Deactivate is called before the widget is removed from the configuration. /// </summary> public void Deactivate() { } /// <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.CountWidgetDialog dialog = new Config.CountWidgetDialog(dataSources, Caption, DataSourceId, FieldName, Value) { Owner = owner }; if (dialog.ShowDialog() != true) return false; // Retrieve the selected values for the properties from the configuration dialog. Caption = dialog.Caption; DataSourceId = dialog.DataSource.Id; Field = dialog.Field; FieldName = Field.Name; Value = dialog.Value; // Re-set the QueryValue after configuration, it will be set again for the new value // in the DoQuery method. QueryValue = null; // The default UI simply shows the values of the configured properties. UpdateControls(); return true; } #endregion #region IDataSourceConsumer Members /// <summary> /// Returns the ID(s) of the data source(s) consumed by the widget. /// </summary> public string[] DataSourceIds { get { return new string[] { DataSourceId }; } } /// <summary> /// Called when a DataSource is removed from the configuration. /// </summary> /// <param name="dataSource">The DataSource being removed.</param> public void DataSourceRemoved(DataSource dataSource) { // Respond to data source being removed. Setting the DataSourceId to null means that the // DataSourceUpdated will not be called again for that data source. DataSourceId = null; CountBlock.Text = "No Data"; } /// <summary> /// Called when a DataSource found in the DataSourceIds property is updated. /// </summary> /// <param name="dataSource">The DataSource being updated.</param> public void DataSourceUpdated(DataSource dataSource) { // Respond to the update from the selected data source using an async method to perform the query. DoQuery(dataSource); } private async void DoQuery(DataSource ds) { // If the widget is deserialized from saved values, make sure the Field is set from the FieldName. if ((Field == null) && (! string.IsNullOrEmpty(FieldName))) { Field = ds.Fields.FirstOrDefault(fld => fld.Name == FieldName); } // Get a query value that takes into account coded value domains. if (string.IsNullOrEmpty(QueryValue)) { QueryValue = GetQueryValue(ds); } // Perform a statistics query on the data source to get the count of requested features. Query countQuery = new Query(string.Format("{0} = {1}", FieldName, QueryValue), new string[] { FieldName }); QueryResult result = await ds.ExecuteQueryStatistic(Statistic.Count, countQuery); // Check the returned results. if ((result != null) && (result.Features != null) && (result.Features.Count == 1)) { object val = result.Features[0].Attributes["Count"]; if (val != null) { CountBlock.Text = val.ToString(); return; } } // If no results were returned, clear the count box. CountBlock.Text = "No Data"; } private string GetQueryValue(DataSource ds) { // Check for coded value domains if ((Field != null) && (Field.Domain != null) && (Field.Domain is client.FeatureService.CodedValueDomain)) { // Translate coded value domain value into appropriate key to use in the query. client.FeatureService.CodedValueDomain codedDomain = Field.Domain as client.FeatureService.CodedValueDomain; KeyValuePair<object,string> valueCode = codedDomain.CodedValues.FirstOrDefault(val => val.Value == Value); string codedForValue = valueCode.Key.ToString(); return codedForValue; } return null; } #endregion } }
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.OperationsCenter Imports client = ESRI.ArcGIS.Client ''' <summary> ''' A Widget is a dockable extension to the ArcGIS Operations Center 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.OperationsCenter.Widget")> _ <ExportMetadata("DisplayName", "CountWidget SDK Sample VB.Net")> _ <ExportMetadata("Description", "WPF Runtime SDK sample widget that counts features containing a specific attribute value.")> _ <ExportMetadata("ImagePath", "/CountWidgetVB;component/Images/Calculator.png")> _ <ExportMetadata("DataSourceRequired", True)> _ <DataContract> _ Partial Public Class CountWidget Inherits UserControl Implements IWidget Implements IDataSourceConsumer ''' <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 ''' <summary> ''' The name of a field within the selected data source. This property is set during widget configuration. ''' </summary> <DataMember(Name:="field")> _ Public Property FieldName() As String Get Return m_FieldName End Get Set(value As String) m_FieldName = value End Set End Property Private m_FieldName As String Private Property Field() As client.Field Get Return m_Field End Get Set(value As client.Field) m_Field = value End Set End Property Private m_Field As client.Field <DataMember(Name:="value")> _ Public Property Value() As String Get Return m_Value End Get Set(value As String) m_Value = value End Set End Property Private m_Value As String Private Property QueryValue() As String Get Return m_QueryValue End Get Set(value As String) m_QueryValue = value End Set End Property Private m_QueryValue As String Public Sub New() InitializeComponent() Value = "0" Caption = "New Count Widget" End Sub Private Sub UpdateControls() CountOfLabel.Text = String.Format("{0}={1}", FieldName, Value) End Sub #Region "IWidget Members" Private _caption As String = "" ''' <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> ''' Activate 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 Sub Activate() Implements IWidget.Activate UpdateControls() End Sub ''' <summary> ''' Deactivate is called before the widget is removed from the configuration. ''' </summary> Public Sub Deactivate() Implements IWidget.Deactivate 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 DataSource)) As Boolean Implements IWidget.Configure ' Show the configuration dialog. Dim dialog As New Config.CountWidgetDialog(dataSources, Caption, DataSourceId, FieldName, Value) 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 FieldName = Field.Name Value = dialog.Value ' Re-set the QueryValue after configuration, it will be set again for the new value ' in the DoQuery method. QueryValue = Nothing ' The default UI simply shows the values of the configured properties. UpdateControls() Return True End Function #End Region #Region "IDataSourceConsumer Members" ''' <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 DataSourceRemoved(dataSource As DataSource) Implements IDataSourceConsumer.DataSourceRemoved ' Respond to data source being removed. Setting the DataSourceId to null means that the ' DataSourceUpdated will not be called again for that data source. DataSourceId = Nothing CountBlock.Text = "No Data" End Sub ''' <summary> ''' Called when a DataSource found in the DataSourceIds property is updated. ''' </summary> ''' <param name="dataSource">The DataSource being updated.</param> Public Sub DataSourceUpdated(dataSource As DataSource) Implements IDataSourceConsumer.DataSourceUpdated ' Respond to the update from the selected data source using an async method to perform the query. DoQuery(dataSource) End Sub Private Async Sub DoQuery(ds As DataSource) ' If the widget is deserialized from saved values, make sure the Field is set from the FieldName. If (Field Is Nothing) AndAlso (Not String.IsNullOrEmpty(FieldName)) Then Field = ds.Fields.FirstOrDefault(Function(fld) fld.Name = FieldName) End If ' Get a query value that takes into account coded value domains. If String.IsNullOrEmpty(QueryValue) Then QueryValue = GetQueryValue(ds) End If ' Perform a statistics query on the data source to get the count of requested features. Dim countQuery As New Query(String.Format("{0} = {1}", FieldName, QueryValue), New String() {FieldName}) Dim result As QueryResult = Await ds.ExecuteQueryStatistic(Statistic.Count, countQuery) ' Check the returned results. If (result IsNot Nothing) AndAlso (result.Features IsNot Nothing) AndAlso (result.Features.Count = 1) Then Dim val As Object = result.Features(0).Attributes("Count") If val IsNot Nothing Then CountBlock.Text = val.ToString() Return End If End If ' If no results were returned, clear the count box. CountBlock.Text = "No Data" End Sub Private Function GetQueryValue(ds As DataSource) As String ' Check for coded value domains If (Field IsNot Nothing) AndAlso (Field.Domain IsNot Nothing) AndAlso (TypeOf Field.Domain Is client.FeatureService.CodedValueDomain) Then ' Translate coded value domain value into appropriate key to use in the query. Dim codedDomain As client.FeatureService.CodedValueDomain = TryCast(Field.Domain, client.FeatureService.CodedValueDomain) Dim valueCode As KeyValuePair(Of Object, String) = codedDomain.CodedValues.FirstOrDefault(Function(val) val.Value = Value) Dim codedForValue As String = valueCode.Key.ToString() Return codedForValue End If Return Nothing End Function #End Region End Class
Copyright © 1995-2014 Esri. All rights reserved.
5/16/2014