[MSCE U16 C#] Create custom group for "Include Items" in Microstation's Report

Introduction

We have introduced groups for selecting items in the Reports->Included Items tab->ItemTypes property. When you click on the "Select an Item Type.." drop-down of "Item Types" dialog, a pop-up appears with groups of Items like "Item Types", "Dgn Elements", "File" etc., & the item list belongs to the selected group. With U16 we are introducing a way to introduce a custom group in this drop-down pop-up control.

SDK Sample

In this blog, I am going to explain how we can use the IClassPickerContentProvider interface to introduce a custom group for "Included Items" w.r.t. SDK sample available at MstnExamples\DgnEC\ReportContentFilter.

In this sample, we have the following schema:

<?xml version="1.0" encoding="UTF-8"?>
<ECSchema schemaName="MachineParts" nameSpacePrefix="DT" version="1.0" description="Test Schema for DGNECPlugin tests" displayLabel="Machine Parts" xmlns="http://www.bentley.com/schemas/Bentley.ECXML.2.0">
	<ECClass typeName="Part" description="An ECClass representing a Gadget" displayLabel="Part" isDomainClass="True">
		<ECProperty propertyName="ID" typeName="string" description="ID" />        
		<ECProperty propertyName="Inventor" typeName="string" description="Part Inventor" />        
		<ECProperty propertyName="Manufacturer" typeName="string" description="Part Manufacturer" />        
		<ECProperty propertyName="Price" typeName="double" description="Part price" />    
	</ECClass>    
	<ECClass typeName="Gadget" description="An ECClass representing a Gadget" displayLabel="Gadget" isDomainClass="True">
		<BaseClass>Part</BaseClass>        
		<ECProperty propertyName="GadgetProperty1" typeName="string" description="Gadget Test Property1" />
		<ECProperty propertyName="GadgetProperty2" typeName="string" description="Gadget Test Property2" />
	</ECClass>
	<ECClass typeName="Widget" description="An ECClass representing a Widget" displayLabel="Widget" isDomainClass="True">
		<BaseClass>Part</BaseClass>
		<ECProperty propertyName="WidgetProperty1" typeName="string" description="Widget Test Property1" />
		<ECProperty propertyName="WidgetProperty2" typeName="string" description="Widget Test Property2" />
	</ECClass>
	<ECRelationshipClass typeName="WidgetHasGadgets" description="WidgetHasGadgets" isDomainClass="True" strength="referencing" strengthDirection="forward">
		<Source cardinality="(1,1)" roleLabel="has Gadgets" polymorphic="True">
			<Class class="Widget" />
		</Source>
		<Target cardinality="(1,N)" roleLabel="are held by Widget" polymorphic="True">
			<Class class="Gadget" />
		</Target>
	</ECRelationshipClass>
</ECSchema>

We have three classes, Part, Widget, and Gadget. Both Widget and Gadget are derived from Part ECClass, and we have a relationship between Widget & Gadget as WidgetHasGadgets. The goal of this sample app is to have a "Machine Parts" group in the UI.

Implementation

Step1: Implement IClassPickerContentProvider

IClassPickerContentProvider is an abstract class, available under Bentley.DgnPlatformNET.ECPresentationNET namespace in Bentley.DgnDisplayNet.dll.

In the constructor, set following:

  1. Supported schema: Define a list of schemas you would like to work on, in m_supportedSchemas.
  2. Include abstract classes: Abstract classes are those classes where IsDomain, IsCustomAttribute & IsStruct is set to false. By default, these classes are excluded by the GetClassList virtual method. However, if you want them to get listed, set m_includeAbstract.
  3. IncludeReferencedBaseClasses: Say, you have created an element with "Widget" & "Gadget" classes. Now, if you are working with the "Include Used Classes" option, you will get "Widget" & "Gadget" in your group. However, if you want to see "Part" ECClass as its base for Widget/Gadget, then you need to set m_includeRefBaseClasses.

Implement Icon & DisplayLabel abstract properties. These properties give Icon & DisplayLabel for UI.

Virtual method IsSchemaSuppported: By default it checks if input schema is available in the m_supportedSchemas list.

Virtual method GetClassList: By default, it returns a list of domain classes available in the input schema. It filters out abstract classes, struct classes, custom attribute classes & hidden classes. For including abstract classes you need to set m_includeAbstract. However, you can override the default behavior.

A sample implementation is:

/*---------------------------------------------------------------------------------**//**
* Implementation of IClassPickerContentProvider
* @bsimethod                                                              Bentley Systems
/*--------------+---------------+---------------+---------------+---------------+------*/
internal class MachinePartsProvider : IClassPickerContentProvider
{
IECSchema m_partSchema = null;
/*---------------------------------------------------------------------------------**//**
* Constructor
* @bsimethod                                                              Bentley Systems
/*--------------+---------------+---------------+---------------+---------------+------*/
public MachinePartsProvider(IECSchema partSchema): base()
    {
    m_partSchema = partSchema;
    //we don't have any abstract classes in our example
    m_includeAbstract = false;
    //when we have "Include ItemTypes -> Options" set to "Include Used Classes", I want to list base classes too.
    //For e.g. I have "Widget" instance in Dgn. Base class of Widget class is "Part". By deafault, I won't see "Part" in UI with above option.
    //To make it avaialble during "Used" option, I need to set m_includeRefBaseClasses to true.
    m_includeRefBaseClasses = true;
    //IsSchemaSuppported method checks for existance of schema name in this list
    m_supportedSchemas.Add(m_partSchema.Name);
    }

/*---------------------------------------------------------------------------------**//**
* Icon for our provider
* @bsimethod                                                              Bentley Systems
/*--------------+---------------+---------------+---------------+---------------+------*/
public override ImageSource Icon 
    {
    get
        {
        return NativeImageProviderAdapterExtension.GetInstance().GetDeliveredImage("PropertySymbol", 16);
        }
    }

/*---------------------------------------------------------------------------------**//**
* Display Label for our provider
* @bsimethod                                                              Bentley Systems
/*--------------+---------------+---------------+---------------+---------------+------*/
public override string DisplayLabel => m_partSchema.DisplayLabel;
}

Step2: Register IClassPickerContentProvider

Register your provider implementation with ClassPickerContentManager.

ClassPickerContentManager.RegisterContentProvider(new MachinePartsProvider(schema));

Step3: Setting up default provider

This step is optional. When you have ItemType libraries, the default provider is the ItemType provider. So, when you launch the "Include Items" dialog and click on the "Select item types" drop-down, the first group you can see is "Item Types". If you don't have ItemType libraries, then the default provider is "Dgn Elements". If you want to promote your own provider as a default provider you can set the "DefaultProvider" property of ClassPickerContentManager.

ClassPickerContentManager.DefaultProvider = <Instance of provider>

You are done with implementation, congratulations!

See it running

With my sample app, you need to execute the following key-ins:

ReportContentFilter Register        : This keyin shows how to implement IClassPickerContentProvider & register it.
ReportContentFilter  CreateData     : This keyin will create data to see IClassPickerContentProvider in action in report dialog.

When you click on the "Select an Item Type.." drop-down of the "Item Types" dialog, a pop-up appears with groups of Items. You should see the "Machine Parts" group, and when you select it, it will show you the list of classes available for reporting.

Thank You!

Note: SDK sample should be available in a release after the U16 build.