• No results found

Building Custom Standards Checker Applications

Advanced – use the Fixes optional dialog

The next step in the development of Standards Checker extensions is to add the  capability to fix issues as they are found during the check process. To do this, the  checker macro needs to call the ShowCheckerErrorWithFixOptions method  which will call the GetFixDetails method.

This method requires the macro to fill a list of possible corrections for the user to  pick from. These “fixes” are then presented to the user in the Standards Checker  user interface. When the user has selected one of the fixes, it is sent back to the  macro which then applies the fix to the element. Once the GetFixDetails  method has returned the value of the ShowCheckerErrorWithFixOptions  method, it is checked and one of the possible actions is taken. The results are then  updated in the report using the StandardsCheckerReport class.

Building Custom Standards Checker Applications

One thing that is not apparent about the Standards Checker is the ability to  extend the functionality to check more than just symbology or level definitions. It  is a framework for processing design files. Application developers can use it to  build custom plug‐in solutions to extend the capabilities. 

An example of this functionality is the ability to read a corporate standard in a  common file that is read at check time. Since symbology is more complex, only  one type of information can be on one level. Custom interpretation of the  information is required.

To build a custom plug‐in, start by creating a new project. In this project, add a  new module. This module will be the entry point for the macro. 

The next step is to insert a new class. In the new class, add the line to implement  the IstandardsChecker interface. 

The next step is to simply add the required method and property signatures for  the interface. Now that the class to handle the standard checking is in place, add a  subroutine in the module to load the class into the standard checker. An 

important note is that the plug‐in can be loaded multiple times. To prevent this,  the macro should unload the class before trying to load the class. To move the  custom plug‐in to the top of the list, use a high priority number (the second  parameter).

Option Explicit

Public oSettingsCollection As SettingCollection Public stdCheckerApp As clsStandardsCheckerImpl Private oSC As IstandardsChecker

Sub OnProjectLoad() AddStdCheckerApp End Sub

Sub AddStdCheckerApp()

' There is nothing to prevent this program from adding 2 standards

‘ checkers. To prevent that from happening, call RemoveAttachmentsChecker

‘ before adding the checker.

RemoveStdCheckerApp

Set oSettingsCollection = New SettingCollection oSettingsCollection.init

Set stdCheckerApp = New clsStandardsCheckerImpl

StandardsCheckerController.AddStandardsChecker stdCheckerApp, 1000 End Sub

Sub RemoveStdCheckerApp()

If Not stdCheckerApp Is Nothing Then

StandardsCheckerController.RemoveStandardsChecker stdCheckerApp Set stdCheckerApp = Nothing

End Sub

By implementing the interface, the class code will appear as follows.

Implements IStandardsChecker

Private Property Get IStandardsChecker_CallForEachModel() As Boolean End Property

Private Sub IStandardsChecker_CreateSettings() End Sub

Building Custom Standards Checker Applications

Private Sub IStandardsChecker_DeleteSettings() End Sub

Private Property Get IStandardsChecker_Description() As String End Property

Private Property Get IStandardsChecker_DialogString() As String End Property

Private Sub IStandardsChecker_EditSettings(ByVal IsReadOnly As Boolean) End Sub

Private Property Get IStandardsChecker_FoundSettings() As Boolean End Property

Private Sub IStandardsChecker_GetFixDetail(Fixes() As String, _ ByVal SelectedFix As Long, _

FixPropertiesLabel As String, _ FixProperties() As String) End Sub

Private Property Get IStandardsChecker_HasSettings() As Boolean End Property

Private Property Get IStandardsChecker_IdentityString() As String End Property

Private Sub IStandardsChecker_RunCheck _ (ByVal ModelToCheck As ModelReference, _ ByVal FirstModel As Boolean, _

ByVal Options As Long) End Sub

Private Property Get IStandardsChecker_VersionString() As String End Property

The macro control properties and methods to note here are HasSettings

EditSettingsFoundSettingCreateSetting, and DeleteSetting. The 

HasSettings method is called by the interface to determine if the macro has  some configurable settings. This will activate the settings button on the user  interface. When the user selects the settings button, the EditSettings method  in the plug‐in will be invoked. Plug‐in settings are stored as a settings element in  the DGN library. If the plug‐in wants to store its settings in the design file, the 

CreateSettings method is invoked by the standards macro. 

The DeleteSettings method is called to let the plug‐in delete the settings  element. The FoundSettings property is used to tell the macro that the plug‐in  has settings and is capable of running. For a macro to work properly, it must set 

FoundSettings to True so that it will be enabled on the user interface. Another  property that a plug‐in should set is CallForEachModel. This tells the macro that  the plug‐in should be called for each model in the design file.

The RunCheck method is called when the macro is invoked to check the elements. 

When the macro invokes the RunCheck method it will pass in the model that is  being checked, some additional information such as if it is the first (or active)  model, and some options that affect the operation of the plug‐in. In the RunCheck  the plug‐in will do most of its work. The plug‐in will need to iterate through the  elements it will check and, if necessary, invoke the user interface. To invoke the  macro user interface, the plug‐in will call the StandardsCheckerController  method, ShowCheckerError method, or ShowCheckerErrorWithFixOptions to  interact with the user. 

ShowCheckerErrorWithFixOptions requires some set up by the macro. The  plug‐in will provide the options that the user can choose from if they want to fix  properties for the TotalProblems and FixedProblems that will be used in the  user interface. If the plug‐in wants to add information to the XML report, it uses  the StandardCheckerReport.AddProblem method. The last parameter for this  method lets the report know if the plug‐in has fixed the element. The RunCheck is  the heart of the plug‐in and, as such, will be the method that requires the most  work when implementing a plug‐in.

The other methods provide some information about the macro to the standard  interface. The Overstraining property lets the macro add information about the 

Building Custom Standards Checker Applications

plug‐ins that are used in processing the file to the report. The IdentityString  property is used to provide a unique name for the plug‐in. The DialogString  property is applied to the main dialog that the user will select from in the macro. 

The Description property is used in report generation from the macro.

To demonstrate this process, this sample macro will use the RunCheck method to  process each element in the file. The settings for this plug‐in will determine the  collection of rules to apply. As each model is passed into the RunCheck method,  the plug‐in will iterate through the elements of that model.

Private Sub IStandardsChecker_RunCheck _ (ByVal ModelToCheck As ModelReference, _ ByVal FirstModel As Boolean, _

ByVal Options As Long)

Dim oEnum As ElementEnumerator Dim oCheckElement As Element

If oSettingsCollection.GetSettings.Count = 0 Then MsgBox "Rerun and Pick Settings file first"

Exit Sub End If

Set oEnum = ModelToCheck.Scan

Do While oEnum.MoveNext

checkSingleElement oEnum.Current Loop

End Sub

Each element is passed into the CheckSingleElement method that is defined in  the plug‐in. The CheckSingleElement method will examine the element and, in  the case of a simple element, it will apply the check to the element. If the element  is complex it will get the components and check them recursively. If the element  passes the check, it allows the StandardsChecker to continue processing.

Sub checkSingleElement(ocheckel As Element) Dim oSubEnum As ElementEnumerator

Dim oSubEl As Element Dim oSetting As Setting

If ocheckel.IsComplexElement Then

Set oSubEnum = ocheckel.AsComplexElement.GetSubElements Do While oSubEnum.MoveNext

checkSingleElement oSubEnum.Current Loop

Else

If ocheckel.IsGraphical And _

ocheckel.Class = msdElementClassPrimary Then Set oSetting = New Setting

oSetting.InitFromElement ocheckel

If oSettingsCollection.check(oSetting) = False Then ReportBadElement ocheckel

End If End If

End If End Sub

If the element fails the check it will be passed to the ReportBadElement method  that the plug‐in defines. The ReportBadElement method will invoke the 

StandardsCheckerShowErrorWithFixOptions method. 

To call this method, the plug‐in will build the fixes array from the possible  combinations that the element uses. The plug‐in uses the GetFixes method to  generate information about the possible fixes. 

' ShowCheckerErrorWithFixOptions calls this to get the data to display ' in the bottom section of the dialog. It also calls this everytime the ' user selects a different row in the list of fixes.

'

Private Sub IStandardsChecker_GetFixDetail _ (Fixes() As String, _

ByVal selectedFix As Long, _ FixPropertiesLabel As String, _ FixProperties() As String)

FixPropertiesLabel = "VBA Example Fix Properties"

Building Custom Standards Checker Applications

Dim oSetting As Setting Dim i As Integer

i = oSettingsCollection.GetSettings.Count

ReDim FixProperties(1 To i, 0 To 2)

For i = 1 To oSettingsCollection.GetSettings.Count FixProperties(i, 0) = "Symbology Settings"

FixProperties(i, 1) = oSettingsCollection.GetSettings.Item(i).ToString FixProperties(i, 2) = oSettingsCollection.GetSettings(i).description Next i

End Sub

When the user selects from the options on the StandardsChecker macro 

interface, the selectedFix option is set to one of the available options. The plug‐

in uses this value to determine the process path. The 

msdStandardsCheckerReplaceChoiceSkip is set when the plug‐in should  simply skip the element. 

The msdStandardsCheckerReplaceChoiceMarkIgnored option lets the plug‐in  mark the element as ignored and the report can have these added so the user can  revisit the element. The msdStandardsCheckerReplaceChoiceFix option tells  the plug‐in that the element can be fixed according to the one of the fixes choices  that the user has selected. The plug‐in can then take the corrective action and use  the AddProblem method to set the element as fixed. Once the element has been  processed the plug‐in will increment the TotalProblems and continue 

processing.

Private Sub ReportBadElement(oelm As Element) Dim scc As StandardsCheckerController Dim rpt As StandardsCheckerReport

Dim response As MsdStandardsCheckerReplaceChoice Dim strDescr As String

Dim opts As MsdStandardsCheckerReplaceOptions

Dim handlerChoice As MsdStandardsCheckerReplaceChoice Dim selFix As Long

Dim columnLabels(0 To 1) As String Dim Fixes() As String

Dim oCurrSetting As New Setting Dim i As Integer

Dim scp As StandardsCheckerProblem

Set currElement = oelm

oCurrSetting.InitFromElement oelm

'StandardsCheckerController.ShowCheckerStatus "Example Status Message"

columnLabels(0) = "Symbology Options"

columnLabels(1) = ""

i = oSettingsCollection.GetSettings.Count

'set up the list of possible fixes for this element ReDim Fixes(0 To i, 0 To 1)

Fixes(0, 0) = oCurrSetting.ToString: Fixes(0, 1) = "Current Settings"

For i = 1 To oSettingsCollection.GetSettings.Count

Fixes(i, 0) = oSettingsCollection.GetSettings.Item(i).ToString Fixes(i, 1) = _

oSettingsCollection.GetSettings.Item(i).description Next i

strDescr = "Bad Element: " & DLongToString(oelm.ID) & _ ElmToInfoString(oelm)

Set scc = StandardsCheckerController

opts = msdStandardsCheckerReplaceOptionCanFix Or _ msdStandardsCheckerReplaceOptionCanIgnore

scc.ShowCheckerErrorWithFixOptions response, selFix, _ strDescr, _

"Fixes List Box Label", _ columnLabels, Fixes, 0, _ opts, _

False

Set rpt = scc.Report

If response = msdStandardsCheckerReplaceChoiceSkip Then Set scp = rpt.AddProblem(strDescr, "My Check", False)

' Record the ElementID because it is the only property of an Attachment

Building Custom Standards Checker Applications

‘ that cannot change. If someone writes a program that processes ' the problem report, they can use the ElementID to be certain the ' program accesses the same attachment that the report refers to.

scp.AddElementID stdCheckerApp.currElement.ID

scp.AddStandard "My Element Checker App", m_libraryID scp.AddVariance "My Symbology", "", oCurrSetting.ToString End If

If response = msdStandardsCheckerReplaceChoiceFix Then Dim aSetting As Setting

Set scp = rpt.AddProblem(strDescr, "My Check", True) 'the last parameter will put the check in the fixed column scp.AddStandard "My Element Checker App", m_libraryID scp.AddAction "Fixed Element Symbology"

scp.AddElementID stdCheckerApp.currElement.ID If selFix > 0 Then

Set aSetting = oSettingsCollection.GetSettings(selFix) Set oelm.Level = _

ActiveDesignFile.Levels.Find(aSetting.Level) oelm.Color = aSetting.Color

oelm.LineWeight = aSetting.Weight End If

oelm.Redraw oelm.Rewrite

scc.FixedProblems = scc.FixedProblems + 1 End If

If response = msdStandardsCheckerReplaceChoiceMarkIgnored Then Set scp = rpt.AddIgnoredProblem("Element ", strDescr, _

"My Check", False)

scp.AddAction "Fixed Element Symbology"

scp.AddElementID stdCheckerApp.currElement.ID

scp.AddStandard "My Element Checker App", m_libraryID scp.AddVariance "My Symbology", "", oCurrSetting.ToString scc.IgnoredProblems = scc.IgnoredProblems + 1

End If

' If the user entered Cancel, tell RunCheck to abort If response = msdStandardsCheckerReplaceChoiceAbort Then

stdCheckerApp.m_aborted = True

scc.TotalProblems = scc.TotalProblems + 1

End Sub

The plug‐in can use the AddedCheckerToStandardsCheckerApps method to add  information to the report and call the AddLibraryToCheckerApp to add a node  to the report. The report’s AddStandardToLibrary method is used to add the  information to the XML data.

Private Sub IStandardsChecker_AddedCheckerToStandardsCheckerApps _ (ByVal ApplicationXMLNode As Object)

Dim rpt As StandardsCheckerReport

' If the program declares these as IXMLDOMNode, then the program ' can add custom XML data to the report. If the program declares them ' as IXMLDOMNode then it must also set a reference to Microsoft XML ' v4.0.

Dim oLibraryNode As Object ' or IXMLDOMNode

Set rpt = StandardsCheckerController.Report

' m_libraryID is output from AddLibraryToCheckerApp.

' It is used later as input to AddStandard

Set oLibraryNode = rpt.AddLibraryToCheckerApp(ApplicationXMLNode, _ StandardsCheckerController.SettingsFile, m_libraryID)

rpt.AddStandardToLibrary oLibraryNode, "Element Checker", "Elements"

End Sub

The framework that is provided in the IStandardsChecker interface allows the  plug‐in developer to interact with the StandardsChecker user interface and build a  seamless extension. The class that implements the IstandardChecker interface  does not need to be developed in VBA — it can be defined in any language that  supports COM.

The StandardsChecker keeps a list of COM objects that it will invoke. The  configuration variable MS_STANDARDCHECKER_APP can be used to automatically  load the plug‐in. If the file name ends in .mvba, it will automatically be added to 

Building Custom Standards Checker Applications

the list of applications that are loaded when the StandardsChecker is invoked by  the user. There are many problems for plug‐ins to solve such as checking for  missing references, etc.

Exercise: Implement a check

1 Implement the simple missing reference checker.

Questions

1 What are the keys to implementing a StandardsChecker extension? 

2 In an implementation of the IStandardsChecker interface, what is the  purpose of calling the HasSettings method?

3 True or False: A class implementing an IStandardsChecker interface  need not be developed in VBA — it can be defined in any language that  supports COM.

Related documents