• No results found

SD11192 Advanced Revit Code Refactoring C# Language Features and Design Patterns Can Help

N/A
N/A
Protected

Academic year: 2021

Share "SD11192 Advanced Revit Code Refactoring C# Language Features and Design Patterns Can Help"

Copied!
20
0
0

Loading.... (view fulltext now)

Full text

(1)

SD11192

Advanced Revit® Code Refactoring—C# Language

Features and Design Patterns Can Help

David Echols – Hankins & Anderson, Inc. – Senior Programmer

Learning Objectives

 Learn how to add extension methods to existing Revit® API objects to add functionality  Learn how to utilize Action class delegates combined with lambda expressions to encapsulate

functionality and simplify code

 Learn how to create LINQ expressions to select and process operations on groups of objects  Learn how to use design patterns to separate code responsibilities and provide uniform code

interfaces

Description

This class will explain how code based on the Revit® software API can be refactored to simplify usage, isolate functionality, and improve maintainability. Class material will cover the use of C# language features and industry best practices to refactor code in Revit® software add-ins and supporting library modules. A detailed look will be taken at extension methods, the Action and Func delegate classes, Microsoft® LINQ, and design patterns. Specific before and after samples of code running in a production add-in will be shown. Extension methods will be used to show how to attach functionality to the

responsible object. Delegates will be used to encapsulate code, making it less complex and more maintainable. LINQ samples will demonstrate selecting and processing groups of objects. Finally, design patterns will be explored that separate command functionality from the user interface and simplify the creation of Revit® elements.

Your AU Experts

Dave Echols began using CADD in 1986 with Prime Medusa. He started with AutoCAD in 1988 using version 2.6. He has written programs for AutoCAD in Autolisp, C/C++, Visual Basic and .NET. He has been developing add-ins for Revit® since 2009 using the Revit® .NET API.

(2)

Introduction

Technology does not stand still and this is certainly true with the Microsoft® .NET framework and the Revit® API. Over the years, Microsoft® has added new APIs and language features. The Revit® API has grown and improved every year with new feature. As add-in code is migrated from one version of Revit® to the next, it is beneficial to review the code and refactor it using new functionality in the API and new functionality and language features in the .NET Framework. This class will present a number of ideas for optimizing and refactoring your add-ins.

Learn how to add extension methods to existing Revit® API objects to add

functionality

The Revit® API provides us with a static interface for accessing the Revit® application, user interface, models and elements. Developers often create methods that extend functionality of specific classes where the source code is not available. These methods can be added to new classes implementing the Decorator or Adapter design patterns or added as static methods to a utility class. With the creation of Extension Methods in the Microsoft® .NET Framework, these methods can now be associated with existing read only classes within static APIs.

Extension Methods used in Hankins & Anderson Revit® add-Ins

Hankins & Anderson has been developing Revit® add-ins since 2009. Over the years each release of Revit® has meant updating and refining this code. In order to make certain code more reusable and organized, a number of extension methods have been created. Below is a list of some of the Revit® API classes utilizing extension methods.

 BasicFileInfo  Document  ViewSheet  ModelPath  Transaction  TransactionGroup

IsOpenedAsCentral extension method for BasicFileInfo

The BasicFileInfo class provides the ability for developers to extract specific basic information about any Revit® model; opened or unopened files. A portion of Hankins & Anderson’s workflow does not permit most users to open a central model directly. BasicFileInfo provides an IsCentral property that informs the developer if a model is a central or local model. The

IsOpenedAsCentral extension method was developed to determine if the document being opened is opening as a central model versus local or detached. The method is used in the DocumentOpening event handler. If the user is not a power user the opening of the document is canceled and a message is displayed to the user. The block below shows the code for the

(3)

public static bool IsOpenedAsCentral(this BasicFileInfo basicInfo, string documentPathName) {

string fileName = Path.GetFileName(documentPathName).ToUpper(); string centralFile = string.Empty;

if (basicInfo.CentralPath.Length > 0) { centralFile = Path.GetFileName(basicInfo.CentralPath).ToUpper(); } return centralFile.ToUpper().Equals(fileName.ToUpper()); }

FIGURE 1: THE BASICFILEINFO.ISOPENEDASCENTRAL EXTENSION METHOD

if (basicInfo.IsOpenedAsCentral(e.PathName)) {

string message = "This model is a Central File. ";

message = string.Format("{0}You do not have permission to open a ", message); message = string.Format("{0}Central File. If you want to open the ", message); message = string.Format("{0}file, make sure the 'Create New Local'", message); message = string.Format("{0} checkbox is checked.", message);

TaskDialogUtility.ShowTaskDialog("Central File Error", message); e.Cancel();

return; }

FIGURE 2: IMPLEMENTING THE BASICFILEINFO.ISOPENEDASCENTRAL EXTENSION METHOD

GetVersionNumber extension method for BasicFileInfo

Hankins & Anderson’s workflow also does not permit most users to upgrade models or families. BasicFileInfo provides a SavedInVersion property and an IsSavedInCurrentVersion property that informs the developer if a model is the same version as the Revit® software. The

GetVersionNumber method parses the return value of the SavedInVersion property and extracts the basic version of the file. The method is used in the DocumentOpening event handler. If the user is not a power user and the software version does not match the file version, the opening of the document is canceled and a message is displayed to the user. The block below shows the code for the method followed by an implementation example.

(4)

public static string GetVersionNumber(this BasicFileInfo basicInfo) {

if (basicInfo == null) return string.Empty;

if (basicInfo.SavedInVersion.IndexOf("2013") > -1) return "2013"; // 2014 & 2015 if (basicInfo.SavedInVersion.IndexOf("2016") > -1) return "2016"; Debug.Assert(false, "Error"); return string.Empty; }

FIGURE 5: THE BASICFILEINFO.GETVERSIONNUMBER EXTENSION METHOD

if (!basicInfo.IsSavedInCurrentVersion) {

string message = string.Format("This model is a \"{0}\" model. ", basicInfo.GetVersionNumber()); message = string.Format("{0}The Revit software version is {1}. ", message, GlobalSettings.RevitVersion);

message = string.Format("{0}This model will not be opened ", message); message = string.Format("{0}by this version of Revit. ", message); message = string.Format("{0}Please use the Revit version", message); message = string.Format("{0} that matches the model.", message); TaskDialogUtility.ShowTaskDialog("Version Mismatch", message); e.Cancel();

return; }

FIGURE 6: IMPLEMENTING THE BASICFILEINFO.GETVERSIONNUMBER EXTENSION METHOD

IsValidForPrintingOrExport extension method for ViewSheet

Hankins & Anderson batch processes PDF exports on all the sheets in its models for Q/A check prints and submittals. The workflow does not permit blank or placeholder sheets to be printed. The View.CanBePrinted property does not provide enough functionality to match this workflow. The IsValidForPrintingOrExport extension method was developed to make sure sheets with text, annotation symbols, schedules and other printable objects are included in the logic. The method is used in the classes that handle the DWF, DWG, Data and PDF exports. The block below shows the code for the method with an implementation sample.

(5)

FIGURE 3: THE VIEWSHEET.ISVALIDFORPRINTINGOREPORT EXTENSION METHOD

foreach (ViewSheet view in viewSheetCollector) {

if (!view.IsValidForPrintingOrExport()) continue; PlotPdfFile(singlePdfOutputFilename, view, item) }

FIGURE 4: IMPLEMENTING THE VIEWSHEET.ISVALIDFORPRINTINGOREPORT EXTENSION METHOD

EndTransaction extension method for TransactionViewSheet

The EndTransaction extension method provides a way to handle a RollBack or Commit operation on an active transaction. It can also handle any type of FailureHandlingOptions if a value is passed into the method. The methods helps minimize code repetition in transactions. The same type of extension method can be used for the TransactionGroup and SubTransaction classes.

public static TransactionStatus

EndTransaction(this Transaction transaction, TransactionType transactionType,

FailureHandlingOptions options = null){ TransactionStatus status = transaction.GetStatus();

if (status != TransactionStatus.Started) return status; switch (transactionType) {

case TransactionType.Commit:

return options != null ? transaction.Commit(options) : transaction.Commit(); case TransactionType.RollBack:

return options != null ? transaction.RollBack(options) : transaction.RollBack();} return TransactionStatus.Error;}

FIGURE 3: THE TRANSACTION.ENDTRANSACTION EXTENSION METHOD public static bool IsValidForPrintingOrExport(this ViewSheet viewSheet)

{

if (viewSheet.IsPlaceholder) return false;

if (viewSheet.GetAllPlacedViews().Count > 0) return true; if (viewSheet.HasSchedules()) return true;

if (viewSheet.HasTextNotes()) return true;

if (viewSheet.HasAnnotationSymbols()) return true; if (!viewSheet.CanBePrinted) return false;

return false; }

(6)

Learn how to utilize Action class delegates combined with lambda expressions to

encapsulate functionality and simplify code

Delegates have been in the .NET Framework from the beginning. With the release of .Net Framework 3.5, Action and Func delegates were added along with Lambda expressions. These features can be difficult to understand and use but can provide developers with ways to encapsulate functionality and simplify their code. The following section will cover the transition of a standard, well used code block in Revit® to an extension method that uses an Action delegate and lambda expression

Transforming Revit® Transactions

Transactions are required in Revit® add-in code when the developer wants to modify elements in the model. It may be one of the most used classes in the API. It makes sense to look at the Transaction workflow and refactor it for simplicity and code safety.

A normal Transaction code block

The code below shows a normal Transaction code block within a try/catch block. The code is clear about what it accomplishes. It catches any errors and rolls back the transaction if errors occur. This code block needs to be used everywhere the enclosed code will modify the Revit® model. This is a repeated pattern for any developer who has written Revit® add-ins.

FIGURE 1: FIRST PASS AT WRITING A TRANSACTION

A Transaction enclosed in a using block

The Transaction class implements the IDisposable interface and must be instantiated with the “new” keyword. This makes it a perfect candidate for placement within a using block. The block will create the object and allow it to be used within the scope of the block. It will close the transaction once execution has exited the block regardless of success or failure of code within the using block. This usage promotes code safety and ensures any unmanaged resources used by the transaction are freed. This also prepares the object for garbage collection. This code block must still be repeated everywhere a transaction is required.

Transaction transaction = null; try

{

transaction = new Transaction(revitDoc, "SingleImport");

if (transaction.Start("SingleImport") != TransactionStatus.Started) return; DoWorkHere();

DoMoreWorkHere(); DoEvenMoreWorkHere(); transaction.Commit(); }

catch (Exception ex) { transaction.RollBack(); }

(7)

FIGURE 1: UTILIZING A USING BLOCK WITH A TRANSACTION

A Transaction code block using a method to reduce complexity

In the example above, there are still multiple code statements within the code block. These statements can and should be refactored into a separate method. The code below shows the refactored block with a single method call. This simplifies the code and makes it much easier to understand.

FIGURE 1: REFACTOR CODE WITHIN THE TRANSACTION BLOCK INTO A SINGLE METHOD

The Transaction code block converted to an extension method

The Transaction constructor always takes a Document object as the first parameter. In order to further refactor the code, it has been converted into an extension method of the Document class. As is, this code is very inflexible because it can perform one function; the DoAllWorkHere() method.

using (Transaction transaction = new Transaction(revitDoc, "SingleImport")) {

try {

if (transaction.Start("SingleImport") != TransactionStatus.Started) return; DoWorkHere();

DoMoreWorkHere(); DoEvenMoreWorkHere(); transaction.Commit(); }

catch (Exception ex) { transaction.RollBack(); } }

using (Transaction transaction = new Transaction(revitDoc, "SingleImport")) {

try {

if (transaction.Start("SingleImport") != TransactionStatus.Started) return; DoAllTheWorkHere();

transaction.Commit(); }

catch (Exception ex) { transaction.RollBack(); } }

(8)

public static void UsingTransaction(this Document revitDoc) {

using (var transaction = new Transaction(revitDoc)) {

try {

if (transaction.Start() != TransactionStatus.Started) return; DoAllTheWorkHere();

transaction.Commit(); }

catch (Exception ex) { transaction.RollBack(); throw; } } }

FIGURE 1: REFACTORING A TRANSACTION TO AN EXTENSION METHOD

The transaction extension method with an added Action delegate parameter

The example in the previous figure must be made more flexible to be of use. Action delegates come to the rescue. An Action delegate is added as a parameter to the extension method. This allows the developer to pass in any method. The Action delegate replaces the DoAllWorkHere() method and provides unlimited flexibility.

public static void UsingTransaction(this Document revitDoc, Action<Transaction> action) {

using (var transaction = new Transaction(revitDoc)) {

try {

if (transaction.Start() != TransactionStatus.Started) return; action(transaction);

transaction.Commit(); }

catch (Exception ex) { transaction.RollBack(); throw; } } }

(9)

UsingTransaction extension method production code

The code examples above have been simplified to explain the transformation process. Below is the production version of the Document.UsingTransaction extension method. It returns a TransactionStatus value instead of a void return.

public static TransactionStatus UsingTransaction(this Document revitDoc,

Action<Transaction, TransactionType, FailureHandlingOptions> action, TransactionType transactionType = TransactionType.Commit,

FailureHandlingOptions failureHandlingOptions = null, string transactionName = null)

{

transactionName = SetTransactionName(transactionName);

using (var transaction = new Transaction(revitDoc, transactionName)) {

try {

TransactionStatus transactionStatus = transaction.Start();

if (transactionStatus != TransactionStatus.Started) return transactionStatus; action(transaction, transactionType, failureHandlingOptions);

return transaction.EndTransaction(transactionType, failureHandlingOptions); }

catch (Exception ex) {

transaction.EndTransaction(TransactionType.RollBack, failureHandlingOptions); throw;

} } }

FIGURE 1: PRODUCTION CODE OF THE USINGTRANSACTION EXTENSION METHOD

Implementing the UsingTransaction extension method

The code snippet below shows how the UsingTransaction extension method is implemented in a PrintPdf external command. The necessary parameters are passed in along with the Action delegate implemented with a Lambda expression.

(10)

FIGURE 1: IMPLEMENTING THE EXTENSION METHOD IN ONE LINE OF CODE

Learn how to create LINQ expressions to select and process operations on groups of

objects

LINQ stands for Language Integrated Query. It made its debut the release of .Net Framework 3.5 and is closely tied to Lambda expressions. The LINQ API allows the use of two different syntax forms; LINQ Query syntax and LINQ Method syntax. Both forms will be covered in this class. Query syntax looks very similar to standard SQL statements and is transformed by the compiler into the Method syntax. The Method syntax provides a number of method calls that operate on collections of objects. These collections are derived from the implementation of the IEnumerable<T> interface of the Enumerable class in the System.Collections.Generic namespace. Interestingly the LINQ methods are themselves extension methods.

LINQ Expression Usage with the REVIT® API

Hankins & Anderson code uses LINQ code in several places. The Query syntax is used most of the time, but the choice of Query versus Method syntax is mostly developer preference. Examples include both styles so an easy comparison can be made between the two. The following sections will cover LINQ expressions in the Revit® user interface and with Revit® filters.

Working with the Revit® Ribbon

Hankins & Anderson utilizes a workflow that does not permit most Revit® users to add a Revit® model link or AutoCAD® DWG link. These functions are managed by the CADD Support

department when a project is first setup. In order to implement this workflow, the Manage Links command is redefined to display a custom dialog box that only allows a user to reload Revit® model links. A side effect of this is the requirement to disable various ribbon items in the Ribbon bar. The following figure show the hierarchy of ribbon objects that must be accessed to enforce this workflow. These classes are not in the RevitAPI or RevitAPIUI assemblies; they are in the AdWindows assembly. Each of these classes implement the IEnumerable<T> interface. private void GeneratePdfFiles(string folder,

List<ElementId> ids) {

try {

string fileName = Path.GetFileNameWithoutExtension(RevitDoc.PathName); Manager = new PlotManager(RevitDoc);

Setup = new PlotSetup(Manager);

RevitDoc.UsingTransaction(TransactionType.RollBack, "Print PDF Command",

(transaction, transactionType) => GeneratePdfFiles(folder, ids, fileName));

}

catch(Exception ex) {

// Log errors and inform user }

(11)

FIGURE 1: THE RIBBON CONTROL OBJECT HIERARCHY

Traversing the Ribbon Hierarchy

The code below shows how LINQ statements can be easily used to traverse the Revit® ribbon control object hierarchy. The ComponentManager.Ribbon property represents the top level ribbon control. The GetRibbonTab method uses a tabID parameter to locate a Tab exposed in the ribbon control. The GetTabPanel method locates the specified panel within the supplied tab. The GetPanelItem method locates the ribbon item in the supplied panel. All three methods use the Query syntax. The from keyword specifies the collection to be queried and the variable that will hold the results. The where keyword specifies the search criteria to find a specific object in the collection. The select keyword executes the query and stores the return value in the result variable.

FIGURE 1: USING LINQ TO TRAVERSE THE REVIT® RIBBON HIERARCHY public RibbonTab GetRibbonTab(string tabId){

var selectedTab = from tab in ComponentManager.Ribbon.Tabs

where tab.Id.ToUpper().Equals(tabId.ToUpper()) select tab; return selectedTab.First();}

public RibbonPanel GetTabPanel(RibbonTab tab, string automationName){ var selectedPanel = from panel in tab.Panels

where panel.Source.AutomationName.ToUpper(). Equals(automationName.ToUpper())

select panel; return selectedPanel.First();} public RibbonItem GetPanelItem(RibbonPanel panel, string id){ var selectedItem = from item in panel.Source.Items

where item.Id.ToUpper().Equals(id.ToUpper()) select item; return selectedItem.First();}

(12)

Using the Method Syntax

The Method syntax is used in the code below to contrast the Query method used above. The code looks very different but returns the same result. The First method is used with a Lambda expression that executes a Boolean predicate matching the where keyword contents shown in the Query syntax sample.

FIGURE 1: LINQ EXPRESSION USING THE METHOD SYNTAX

Implementing code to disable ribbon items

The code below shows how the preceding methods are used to locate specific objects in the Revit® ribbon bar and disable them during application initialization. Additional code not shown redefines the Manage Links command and replaces it with a custom workflow.

FIGURE 1: CODE DISABLING RIBBON ITEMS public RibbonTab GetRibbonTabX(string tabId){ return ComponentManager.Ribbon.Tabs.First(tab => tab.Id.ToUpper().Equals(tabId.ToUpper()));}

public RibbonPanel GetTabPanelX(RibbonTab tab, string name){ return tab.Panels.First(panel =>

panel.Source.AutomationName.ToUpper(). Equals(name.ToUpper()));}

public RibbonItem GetPanelItemX(RibbonPanel panel, string id){ return panel.Source.Items.First(item =>

item.Id.ToUpper().Equals(id.ToUpper()));}

public static class ApplicationInitializedHandler {

public static void Handler(object sender, ApplicationInitializedEventArgs e) {

RibbonTab tab = RevitUi.Instance.GetRibbonTab("Insert");

RibbonPanel linkPanel = RevitUi.Instance.GetTabPanel(tab, "Link"); RibbonItem revitLinkitem = RevitUi.Instance.GetPanelItem(linkPanel,

"ID_RVTDOC_LINK"); revitLinkitem.IsEnabled = false;

RibbonItem ifcLinkitem = RevitUi.Instance.GetPanelItem(linkPanel, "ID_IFC_LINK"); ifcLinkitem.IsEnabled = false;

} }

(13)

Working with Revit® filters - Views

Filters in the Revit® API support the use of LINQ expressions because the *Collector* classes return IEnumerable<T> object collections. Using the FilterElementCollector, it is very easy to obtain a collection of views with the OfClass method. The figure below implements the method to find all ViewSheets within the model. A LINQ expression using the Query syntax further narrows the query results by finding ViewSheets with a SheetNumber that starts with “E2”.

using (FilteredElementCollector collector = new FilteredElementCollector(revitDoc)) {

using (ElementClassFilter viewSheetFilter = new ElementClassFilter(typeof(ViewSheet))) {

var selectedViewSheets = from vs in collector

.WherePasses(viewSheetFilter) .Cast<ViewSheet>()

where vs.SheetNumber.StartsWith("E2") select vs;

foreach (ViewSheet sheet in selectedViewSheets) {Debug.Print(sheet.SheetNumber);}

} }

FIGURE 1: LOCATING VIEWSHEETS WITH SPECIFIC SHEET NUMBER CRITERIA

Working with Revit® filters - TextNotes

Expanding on the example above, the code below uses a FilteredElementCollector to return the ElementIds of all DraftingViews in the model. A second FilteredElementCollector collection uses the OfClass method to collect all TextNote objects. These TextNote objects are further filtered to a specific drafting view using the OwnedByView method. The result is a collection of all TextNote objects in a view. The where keyword of the LINQ expression filters the list of

TextNote to all objects with a TextNoteType that is one eighth inch (“1/8”) in height. This type of workflow supports locating specific text and changing the TextNoteType to a different type that matches company standards.

(14)

using (FilteredElementCollector viewCollector = new FilteredElementCollector(revitDoc)){

foreach (ElementId viewId in viewCollector.OfClass(typeof(ViewDrafting)) .ToElementIds())

{

using (FilteredElementCollector collector = new FilteredElementCollector(revitDoc)) {

var selectedTextNotes = from tn in collector.OfClass(typeof(TextNote)) .OwnedByView(viewId) .Cast<TextNote>()

where tn.TextNoteType.Name.IndexOf("1/8")>-1 select tn;

foreach (TextNote textNote in selectedTextNotes) { // Change to 3/32" }}}}

FIGURE 1: FILTERS IN TEXTNOTES BY A SPECIFIC TEXTNOTETYPE

Learn how to use design patterns to separate code responsibilities and provide

uniform code interfaces

The book “Design Patterns: Elements of Reusable Object-Oriented Software” by Gamma, Helm, Johnson & Vlissides was first published in 1995. Design patterns in software architecture have been around for over 2 decades. From the book: “A design pattern names, abstracts and identifies the key aspects of a common design structure that makes it useful for creating a reusable object-oriented design.”. 1

It is safe to say that the developers of the Revit® API have used design patterns in the implementation of the API. Using a tool like Red Gate’s .Net Reflector, it is easy to spot these design patterns. Proxies are used to handle unmanaged resources and Adapters are used to transform the interface presented by the proxy objects into the public interface presented in the API. The

Autodesk.Revit.DB.ExtensibleStorage namespace uses the Builder pattern to build fields and schemas. Developers using the Revit® API can also use design patterns in their code.

Design Patterns in Hankins & Anderson code

Hankins & Anderson uses design patterns in its code to organize and isolate functionality and make the code easier to maintain. The list below shows some of the patterns in use.

 Singleton  Command  Factory  Adapter  Builder

Command and Factory patterns used together

The image below give an overview of how a CommandFactory object executes a

(15)

interacts with Revit® to produce the PDF files. This is a key benefit of using the factory pattern with the command pattern. Internally, Revit® probably uses a similar mechanism to manage the loading and execution of external commands.

FIGURE 1: OVERVIEW OF THE COMMANDFACTORY/COMMAND IMPLEMENTATION

The IExternalCommand code

In the code below, the IExternalCommand calls the dialog box to collect information from the user. The user selects the sheets to be exported and clicks the OK button. The CommandFactory is then called to initiate the PrintPdfCommand. The first step is to register the PrintPdfCommand which instantiates the object and stores it in an internal list of registered commands. The next step is to execute the command with the data collected from the dialog box stored in an object array. When the command finishes, the CommandFactory unregisters the command.

(16)

public Result Execute(ExternalCommandData data, ref string msg,

ElementSet elements) {try

{

Document revitDoc = data.Application.ActiveUIDocument.Document; AssemblyName name = Assembly.GetExecutingAssembly().GetName(); using (PrintPdfForm frm = new PrintPdfForm(name,

revitDoc)) {

CommandFactory.GetInstance().Register(typeof (PrintPdfCommand), "Print PDF Files"); CommandFactory.GetInstance().Execute("Print PDF Files",

new object[] {commandData, elements, frm.OutputFolder, frm.SelectedSheets}); CommandFactory.GetInstance().Unregister(typeof (PrintPdfCommand), "Print PDF Files"); } return Result.Succeeded; }

catch (Exception ex) { message = ex.Message; } return Result.Failed; }

FIGURE 1: IMPLEMENTATION OF THE IEXTERNALCOMMAND EXECUTE METHOD

The CommandFactory class is also a Singleton

The code below shows the CommandFactory class defined using the Singleton design pattern. This pattern only allows one instance of an object. Within the loaded Revit® add-in, one CommandFactory object will manage the loading and execution of all Command objects. No thread locking is needed because only one command at a time can run in the Revit® user interface.

(17)

public sealed class CommandFactory {

private static readonly CommandFactory _instance = new CommandFactory(); private CommandFactory()

{

Commands = new List<ICommand>();

RegisteredCommands = new List<ICommand>(); }

public static CommandFactory GetInstance() { return _instance; } // ... }

FIGURE 1: THE COMMANDFACTORY CLASS IMPLEMENTED AS A SINGLETON

Adapter pattern used with DetailLines

Hankins & Anderson has an external command that creates a drafting view from an AutoCAD® DWG file. This required developing drawing routines for the DetailCurve elements supported in a drafting view. To create a DetailLine using the Revit® API, a Line object must be instantiated and used with the Document.Create property to create the new element. The following figure shows an overview of the classes involved.

FIGURE 1: CLASS DIAGRAMS OF HADETAILLINE VERSUS REVIT® API CLASSES

Creating a HaDetailLine object

The HaDetailLine adapter contains a single static Create method that instantiates a new DetailLine element and adds it to a view. Using an Adapter class, a single line of code can be used to create the element. This much simpler that using the native Revit® API. Other elements,

(18)

such as the TextNote element are more complex. The following figure shows how the Create method works by providing a clean wrapper for creating a DetailLine.

public static HaDetailLine Create(XYZ startPoint, XYZ endPoint, View currentView){ HaDetailLine newHaDetailLine = new HaDetailLine();

using (Line newLine = Line.CreateBound(startPoint, endPoint)){ using (DetailLine detailLine =

currentView.Document.Create

.NewDetailCurve(currentView, newLine) as DetailLine){ if (detailLine != null){

newHaDetailLine._detailLine = detailLine; newHaDetailLine.IsInitialized = true;}}} return newHaDetailLine;}

FIGURE 1: THE STATIC CREATE METHOD OF THE HADETAILLINE CLASS

Creating a DetailLine from an AutoCAD® AcDbLine entity

The following DrawLine method shows how easy it is to create a DetailLine element using data from An AutoCAD® Line entity. The HaDetailLine adapter object is created with the static Create method using AutoCAD® line data. A line style is assigned by performing a lookup into a

(19)

private bool DrawLine(AcadEntity entity, AcadEntity parentEntity, XYZ startPoint, XYZ endPoint){

try {

HaDetailLine newLine = HaDetailLine.Create(startPoint, endpoint, ActiveView); newLine.AssignLineStyle(GlobalSettings.ImportLineWeights [GetColorIndex(entity, parentEntity)], HaLineStyles.GetLinePatternType(entity, parentEntity)); return true; }

catch (ElementCreationException ex) { // Log Errors } catch (Exception ex) { // Log Errors }

return false;}

(20)

References

The following references were used in the preparation of this handout.

1. Gamma, E., Helm, R., Johnson, R., & Vlissides, J. (1995). Introduction: Section 1.1. In Design patterns: Elements of reusable object-oriented software (p. 3). Reading, MA: Addison-Wesley.

References

Related documents

• The development of a model named the image based feature space (IBFS) model for linking image regions or segments with text labels, as well as for automatic image

National Conference on Technical Vocational Education, Training and Skills Development: A Roadmap for Empowerment (Dec. 2008): Ministry of Human Resource Development, Department

The UK Market Unit comprises private health insurance, wellbeing services, care homes, out of hospital healthcare services and a complex care hospital in London.. In 2012,

It was decided that with the presence of such significant red flag signs that she should undergo advanced imaging, in this case an MRI, that revealed an underlying malignancy, which

19% serve a county. Fourteen per cent of the centers provide service for adjoining states in addition to the states in which they are located; usually these adjoining states have

Field experiments were conducted at Ebonyi State University Research Farm during 2009 and 2010 farming seasons to evaluate the effect of intercropping maize with

This essay asserts that to effectively degrade and ultimately destroy the Islamic State of Iraq and Syria (ISIS), and to topple the Bashar al-Assad’s regime, the international