Hi!
I'm still new to Autodesk plugins and since I didn't find an answer to my question I wanted to know if I could have 2 or more external commands in a single class?
I'm trying to connect Revit and SAP PLM and my goal is to create commands for Material upload, download and update. Since I'm more of a Java person I'm used to creating a class and class/object related methods in that class. I guess what I really want to know is that if I can use class methods as commands? Or should I just create multiple classes in a single .cs file and make each class implement IExternalCommand?
If I didn't phrase my question well enough try to be understanding because English is not my first language. Also if you have any questions about what I'm talking here don't hesitate to ask them in the comments.
Hi
Briefly, You cannot represent commands with methods within a single class.
But actually you don't need to implement IExternalCommand interface over and over again either. I would encourage you to investigate an approach where you have a base class that implements IExternalCommand. Within the Excute() method of this base class, you would call some (of your own made) virtual methods which are overriden in the deriving classes. The behaviour of certain command depends from the implementation of these methods.
So for example something like follows:
class BaseCommand : IExternal command { public virtual Result Execute(...) { this.DoSomething(); } protected abstract bool DoSomething(); } class Upload : BaseCommand { protected override bool DoSomething() { // Uploading stuff... } } class Download : BaseCommand { protected override bool DoSomething() { // Downloading stuff... } }
This is something similar what is also called as a "Template method". See more from Template method pattern in wiki.
I keep getting the error not all code paths return a value.
Also isn't the "this.xxx" only meant for Macros?
And shouldn't it be an abstract class if it contains an abstract bool?
I still cant seem to get the code to compile without errors
using Autodesk.Revit.UI;
using Autodesk.Revit.DB;
using System;
using Autodesk.Revit.Attributes;
namespace Blank
{
[Transaction(TransactionMode.Manual)]
[Regeneration(RegenerationOption.Manual)]
abstract class CmdCombo : IExternalCommand
{
public virtual Result Execute(ExternalCommandData commandData, ref string message, ElementSet elements)
{
DoSomething();
return Result.Succeeded;
}
protected abstract bool DoSomething();
}
class Upload : CmdCombo
{
protected override bool DoSomething()
{
//Uploading stuff...
UIApplication uiapp = commandData.Application;
// Built-in Revit commands are listed in the
// PostableCommand enumeration
RevitCommandId id_built_in
= RevitCommandId.LookupPostableCommandId(
PostableCommand.ExportCADFormatsDXF);
uiapp.PostCommand(id_built_in);
}
}
class Download : CmdCombo
{
protected override bool DoSomething()
{
// Downloading stuff...
Autodesk.AutoCAD.Interop.AcadApplication a = new Autodesk.AutoCAD.Interop.AcadApplication();
a.Visible = true;
Autodesk.AutoCAD.Interop.AcadDocument doc
= a.Documents.Application.ActiveDocument;
double[] stpoint = new double[3];
double[] enpoint = new double[3];
stpoint[0] = 5;
stpoint[1] = 5;
stpoint[2] = 0;
enpoint[0] = 12;
enpoint[1] = 3;
enpoint[2] = 0;
doc.ModelSpace.AddLine(stpoint, enpoint);
doc.SaveAs("C:/tmp/testline.dwg");
a.Quit();
}
}
}
The DoSomething method doesn't have a commandData parameter. You need to modify the abstract method definition and the definitions of the overrides to add that parameter and pass that parameter to where you call it in the Execute method.
Do you mean by adding it in between the brackets like so? I've gotten rid of the compiling error with CommandData for the Upload Class but It's still saying not all paths return a value though.
using Autodesk.Revit.UI;
using Autodesk.Revit.DB;
using System;
using Autodesk.Revit.Attributes;
namespace Blank
{
[Transaction(TransactionMode.Manual)]
[Regeneration(RegenerationOption.Manual)]
abstract class CmdCombo : IExternalCommand
{
public virtual Result Execute(ExternalCommandData commandData, ref string message, ElementSet elements)
{
DoSomething(commandData);
return Result.Succeeded;
}
protected abstract bool DoSomething(ExternalCommandData commandData);
}
class Upload : CmdCombo
{
protected override bool DoSomething(ExternalCommandData commandData)
{
//Uploading stuff...
UIApplication uiapp = commandData.Application;
// Built-in Revit commands are listed in the
// PostableCommand enumeration
RevitCommandId id_built_in
= RevitCommandId.LookupPostableCommandId(
PostableCommand.ExportCADFormatsDXF);
uiapp.PostCommand(id_built_in);
}
}
class Download : CmdCombo
{
protected override bool DoSomething(ExternalCommandData commandData)
{
// Downloading stuff...
Autodesk.AutoCAD.Interop.AcadApplication a = new Autodesk.AutoCAD.Interop.AcadApplication();
a.Visible = true;
Autodesk.AutoCAD.Interop.AcadDocument doc
= a.Documents.Application.ActiveDocument;
double[] stpoint = new double[3];
double[] enpoint = new double[3];
stpoint[0] = 5;
stpoint[1] = 5;
stpoint[2] = 0;
enpoint[0] = 12;
enpoint[1] = 3;
enpoint[2] = 0;
doc.ModelSpace.AddLine(stpoint, enpoint);
doc.SaveAs("C:/tmp/testline.dwg");
a.Quit();
}
}
}
You need to add `return true;` to the end of each of your DoSomething methods.
Ok I get it to compile now. Everything builds correctly but I'm getting an error when I try to launch the command from my Custom Button within Revit.
"Revit cannot run the external command "Combo". Contact the ...
System.MissingMethodException
Constructor on Type 'Bank.Combo' not found "
The following...
"Bank.Combo"
Seems to have multiple typos / differences when compared to code above:
Bank"-> Blank
Combo -> CmdCombo"