Introduction
With Microstation CE U14, we are introducing a way to listen to DgnEC instance and relationship change events at both Native DgnPlatform and Managed DgnPlatformNet layers w.r.t. the following interfaces: Bentley::DgnPlatform::IDgnECChangeListener & Bentley.DgnPlatformNet.DgnEC.IDgnECNetChangeListener
SDK Sample
In this blog, I am going to explain how can we use IDgnECChangeListener and IDgnECNetChangeListener interfaces w.r.t. following SDK example
Native EC change events
Step:1: Implement IDgnECChangeListener
This interface is available in DgnPlatform/DgnECChangeManager.h. You need to register an implementation of IDgnECChangeListener with DgnECManager. The constructor takes *Priority* as a input. This variable defines order for your listener in ascending order.
struct BaseChangeProcessor : Bentley::DgnPlatform::IDgnECChangeListener { public: BaseChangeProcessor (int priority) : IDgnECChangeListener (priority) { DgnECManager::GetManager ().RegisterDgnECChangeListener (this); } virtual ~BaseChangeProcessor () { this->Clear (); DgnECManager::GetManager ().UnregisterDgnECChangeListener (this); }
Step :2: Implement AcceptChangeFor.
Once something changed in EC system, you will get callback in this method. Here you get an ECClass which is undergoing changes and transaction type. TransactionType is an enum that shows whether transaction is regular transaction or UnDo/ReDo transaction. If you are interested to respond to changes for this particular class, you need to return true.
virtual bool AcceptChangeFor (ECN::ECClassCR ecClass, TransactionType transactionType) override { return ecClass.GetSchema ().GetFullSchemaName ().Equals (SCHEMA); } //Or virtual bool AcceptChangeFor (ECN::ECClassCR ecClass, TransactionType transactionType) override { return ItemTypeLibrary::IsItemTypeSchema (ecClass.GetSchema ()); }
Step 3: Implement DgnECInstancesChanged
If you have responded +ve in AcceptChangeFor, you will get this callback for any changes in DgnEC instances. It gives you collection of change DgnEC instance records.
virtual void DgnECInstancesChanged (DgnInstanceChangeRecords& changedRecords, DgnFileR file, TransactionType transactionType) override { for (auto const& nRecord : changedRecords) { switch (nRecord->GetChangeState ()) { case DgnECChangeType::Added: m_addedInstances.push_back (nRecord); break; case DgnECChangeType::Modified: m_modifiedInstances.push_back (nRecord); break; case DgnECChangeType::Deleted: m_deletedInstances.push_back (nRecord); break; default: m_existingInstances.push_back (nRecord); break; } } }
Understanding DgnInstanceChangeRecords
DgnInstanceChangeRecords are collection of non copy-able class, DgnInstanceChangeRecord. It has following important members.
For now, we cannot provide an instance before change because of performance and change tracking overhead.
Step 4: Implement RelationshipsChanged
If you have responded +ve in AcceptChangeFor, you will get this callback for any changes in DgnEC relationships. It gives you collection of changed relationship records.
virtual void RelationshipsChanged (DgnRelationChangeRecords& changedRecords, DgnFiles& files, TransactionType transactionType) override { for (auto const& nRecord : changedRecords) { switch (nRecord->GetChangeState ()) { case DgnECChangeType::Added: m_addedRelations.push_back (nRecord); break; case DgnECChangeType::Modified: m_modifiedRelations.push_back (nRecord); break; case DgnECChangeType::Deleted: m_deletedRelations.push_back (nRecord); break; default: m_existingRelations.push_back (nRecord); break; } } }
Understanding DgnRelationChangeRecords
DgnRelationChangeRecordsare collection of non copy-able class, DgnRelationChangeRecord. It has following important members.
Step 5: Understanding SetIsHandled/GetIsHandled
You can have multiple listeners. During instance or relationship changed callback, if you want other listeners should not receive further callbacks for a particular changed block, set <your listener>->SetIsHandled (true); For e.g. you have two listeners A and B. Now, change property from ElementInfo dialog. Based on priority, say you get callback in A. Set *SetIsHandled*, you will not get callback in B.
Managed EC change events
Step:1: Implement IDgnECNetChangeListener
This interface is available in Bentley.DgnPlatformNet.dll. You need to register implementation of your listener with DgnECChangeAdapter. For Priority, you need to implement GetPriority.
internal abstract class BaseChangeProcessor: IDgnECNetChangeListener { public BaseChangeProcessor() { DgnECManager.Manager.GetDgnECChangeAdapter().RegisterListener(this); } public override int GetPriority() { return 5; }
Same as native API. Here we get ChangeClassification instead of transaction type enum. Purpose of this enum is exactly same as it's native counter part.
public override bool AcceptChangeFor(IECClass ecClass, ChangeClassification transactionType) { if (ecClass.Schema == null) return false; return ecClass.Schema.Name == SCHEMA; } //or public override bool AcceptChangeFor(IECClass ecClass, ChangeClassification transactionType) { return ItemTypeLibrary.IsItemTypeSchema(ecClass.Schema.FullName); }
On any changes in DgnEC instances, you will get this callback.
public void DgnECInstancesChanged(IList<DgnInstanceChangeRecord> changeRecords, DgnFile file, ChangeClassification transactionType) { AddInstanceChanges(changeRecords); } protected void AddInstanceChanges(IList<DgnInstanceChangeRecord> changeRecords) { foreach(var record in changeRecords) { switch (record.ChangeState) { case ChangeSetElementState.New: m_addedInstances.Add(record); break; case ChangeSetElementState.Modified: m_modifiedInstances.Add(record); break; case ChangeSetElementState.Deleted: m_deletedInstances.Add(record); break; default: m_existingInstances.Add(record); break; }; } }
Understanding DgnInstanceChangeRecord
ChangeSetElementState ChangeState: Indicates whether available instance in record is added, modified or deleted.IECClass ClassOfChange: ECClass under changeIDgnECInstance AfterChange: Modified or added instance after change. This will be null for deleted change state.string DeletedInstanceId: If change state is deleted, you can get deleted instanceId.
For now, we cannot provide instance before change because of performance and change tracking overhead.
On any change in DgnEC relationships, you will get this callback.
public void RelationshipsChanged(IList<DgnRelationChangeRecord> changeRecords, IList<DgnFile> files, ChangeClassification transactionType) { AddRelationChanges(changeRecords); } protected void AddRelationChanges(IList<DgnRelationChangeRecord> changeRecords) { foreach(var record in changeRecords) { switch (record.ChangeState) { case ChangeSetElementState.New: m_addedRelations.Add(record); break; case ChangeSetElementState.Modified: m_modifiedRelations.Add(record); break; case ChangeSetElementState.Deleted: m_deletedRelations.Add(record); break; default: m_existingRelations.Add(record); break; }; } }
Understanding DgnRelationChangeRecord
DgnRelationChangeRecordsare collection of non copyable class, DgnRelationChangeRecord. It has following important members.
Ideally, you should use RelationshipInfo. It will save performance overhead of serializing native source/target instances to managed.
Step 5: Understanding IsHandled
It works same as native Set/Get IsHandled method.
Some useful tips
That's all. Please try this in the MicroStation U14 SDK and give us feedback. Thanks a lot.
Thanks for providing this example of forthcoming (May 2020) technology!