ProGuide Combo boxes - Esri/arcgis-pro-sdk GitHub Wiki
Language: C#
Subject: Framework
Contributor: ArcGIS Pro SDK Team <[email protected]>
Organization: Esri, http://www.esri.com
Date: 10/06/2024
ArcGIS Pro: 3.4
Visual Studio: 2022
This ProGuide explains the step by step process on how to declare a combo box. However this is for reference only. The recommended approach is to run the combo box item template.
-
How to declare a Combo box
-
Populating a Combo box
- Determine when a selection occurs
- Customizing Content with a Data Template File
Combo box controls are similar to Edit box controls, but also provide a drop-down list. They are declared in DAML using the comboBox element within the controls container.
A Combo box is declared using the id, className, and sizeString attributes. It can be configured to appear with or without a caption. Use the readOnly attribute to set the Combo box to read-only. Use the condition attribute to assign a predefined condition to control the enabled state.
The sizeString attribute is used to establish the width of the control. This string is representative of the kind of input that appears in the control. The sizeString attribute can also be updated at runtime.
Use the ItemHeight and ItemWidth attributes to customize the width and height of the control on the ribbon. Use the rows attribute to set the number of rows that display on the drop-down list.
The Combo box is referenced on a group using the comboBox control with the refID attribute.
<groups>
<group>
<labelControl refID="esri_mapping_labelClassFieldLabel" size="small"/>
<comboBox refID="esri_mapping_labelClassExpressionComboBox" size="small" />
<button refID="esri_mapping_labelClassExpressionButton" size="small" />
</group>
</groups>
<controls>
<comboBox id="esri_mapping_labelClassExpressionComboBox"
condition="esri_mapping_singleFeatureLayerSelectedCondition" caption=""
className="Labeling.Ribbon.LabelClassExpressionComboBox" isEditable="false" isReadOnly="true"
itemWidth="140">
<tooltip heading="Label Class">Set the label expression for the current label class.
<disabledText></disabledText></tooltip>
</comboBox>
</controls>
The className attribute references the Combo box implementation. This is a class that inherits from the ArcGIS.Desktop.Framework.Contracts.ComboBox base class. Combo boxes populate dynamically at runtime by this derived class. The list consists of ComboBoxItem objects, each with a caption, optional image, and generic tag.
internal class LabelClassExpressionComboBox : ArcGIS.Desktop.Framework.Contracts.ComboBox
{
public LabelClassExpressionComboBox ()
{
// TODO : add code here to dynamically populate the combobox
}
}
Combo boxes populate dynamically at runtime by their derived class. The Combo box list consists of ComboBoxItem
objects, each with a caption, optional image, and generic tag.
internal class MyComboBox : ArcGIS.Desktop.Framework.Contracts.ComboBox
{
public MyComboBox()
{
this.Add(new ComboBoxItem("Item1"));
this.Add(new ComboBoxItem("Item2"));
this.Add(new ComboBoxItem("Item3"));
this.Add(new ComboBoxItem("Item4"));
this.Add(new ComboBoxItem("Item5"));
}
}
The base ArcGIS.Desktop.Framework.Contracts.ComboBox class provides an OnSelectionChange method that can be overridden by your implemented class to provide a custom action when the user selects an item from the Combo box. See the code snippet below for how this looks.
internal class MyComboBox : ArcGIS.Desktop.Framework.Contracts.ComboBox
{
public MyComboBox()
{
this.Add(new ComboBoxItem("Item1"));
this.Add(new ComboBoxItem("Item2"));
this.Add(new ComboBoxItem("Item3"));
this.Add(new ComboBoxItem("Item4"));
this.Add(new ComboBoxItem("Item5"));
}
protected override void OnSelectionChange(ComboBoxItem item)
{
// TODO - add specific custom code here
}
}
There may be situations where the combobox content needs to be styled beyond what the out-of-the-box ComboBoxItem
class can provide. In this case you will need to provide a dataTemplateFile
containing the xaml styling for your combo box items. This is a similar pattern to the use of a data template file for customizing gallery content items.
To use a data template file with a combo box, first create a WPF resource dictionary and add it to the add-in Visual Studio project. Within the resource dictionary, create a WPF DataTemplate defining the desired item styling. There are no restrictions to the WPF styling though keep in mind that the items should be styled to fit within the combo box edit box and drop down. In this example, custom styling is being applied to show a custom "ColorItem" class which will be used to make a color picker combo on the ribbon:
<ResourceDictionary ...>
<DataTemplate x:Key="PickAColorCombo">
<DockPanel >
<TextBlock Width="60" Text="{Binding Name}" DockPanel.Dock="Left"
VerticalAlignment="Center"/>
<Border BorderBrush="{DynamicResource Esri_Black}"
BorderThickness="1"
CornerRadius="2"
Margin="0,1,0,1"
Background="{Binding Brush}"
Width="40"
Height="20"
DockPanel.Dock="Right">
</Border>
</DockPanel>
</DataTemplate>
</ResourceDictionary>
Each item in the combo will have a color swatch (on the right) along with the name of the/its color (on the left).
In the Config.daml, the <comboBox ...>
daml entry is modified with a templateID
and dataTemplateFile
attribute. The WPF DataTemplate x:Key is used as the comboBox templateID
and the dataTemplateFile
contains the pack uri of the resource dictionary containng the custom item styling:
<controls>
<!-- add your controls here -->
<comboBox id="Acme_ChangeSymbolColor_PickAColor" caption="Pick a color"
className="PickAColorCombo" itemWidth="110" extendedCaption="Pick a color"
dataTemplateFile="pack://application:,,,/AcmeAddin;component/CustomComboResources.xaml"
templateID="PickAColorCombo" isEditable="false" isReadOnly="true" resizable="true">
<tooltip heading="Tooltip Heading">Pick a color from the list of colors<disabledText /></tooltip>
</comboBox>
On initialization, the combo box should populate its list with the relevant items (in this case the custom ColorItem
instances) same as when using the out-of-the-box ComboBoxItem
:
//custom color item shown in the combo
internal class ColorItem {
private Color _color = System.Windows.Media.Colors.Black;
private SolidColorBrush _brush = null;
private string _name = "";
public ColorItem(Color color) {
Initialize(color);
}
private void Initialize(Color color) {
_color = color;
_name = NameFromColor(_color);
_brush = new SolidColorBrush(_color);
}
#region Properties
public string Name => _name;
public SolidColorBrush Brush => _brush;
public Color Color => _color;
#endregion
internal string NameFromColor(Color col) {
PropertyInfo colorProperty = typeof(System.Windows.Media.Colors).GetProperties()
.FirstOrDefault(p => Color.AreClose((Color)p.GetValue(null), col));
return colorProperty != null ? colorProperty.Name : "";
}
}
//initialize the list of available colors for the picker (in the module in this case)...
internal class Module1 : Module {
private List<ColorItem> _colorList = new List<ColorItem>();
private ColorItem _colorItem = null;
...
protected override bool Initialize() {
_colorList = new List<ColorItem>();
_colorList.Add(new ColorItem(Colors.Red));
_colorList.Add(new ColorItem(Colors.Green));
_colorList.Add(new ColorItem(Colors.Blue));
_colorList.Add(new ColorItem(Colors.Orange));
_colorList.Add(new ColorItem(Colors.Yellow));
_colorList.Add(new ColorItem(Colors.Purple));
return true;
}
public List<ColorItem> ColorList => _colorList;
public ColorItem SelectedColorItem {
get {
return _colorItem;
}
set {
_colorItem = value;
}
}
...
//populate the combo box with the custom color items
//and handle the combo box item selection
internal class PickAColorCombo : ComboBox {
private bool _isInitialized;
public PickAColorCombo() {
UpdateCombo();
}
private void UpdateCombo() {
//populate the combobox with your desired items
if (_isInitialized)
SelectedItem = ItemCollection.FirstOrDefault();
if (!_isInitialized) {
Clear();
//Add the custom color items to the combobox
foreach(var colorItem in Module1.Current.ColorList) {
Add(colorItem);
}
_isInitialized = true;
}
Enabled = true; //enables the ComboBox
SelectedItem = ItemCollection.FirstOrDefault();
}
protected override void OnSelectionChange(object item) {
if (item == null) return;
//handle the color item selection
Module1.Current.SelectedColorItem = (ColorItem)item;
}
}
The final product: