Pass ExternalCommandData to a called class

Pass ExternalCommandData to a called class

Anonymous
Not applicable
2,531 Views
4 Replies
Message 1 of 5

Pass ExternalCommandData to a called class

Anonymous
Not applicable

I have a class to reloadimages from a source.  I have it working fine if the code is executed from the Commad class.  I have moved the code into it's own class, and following the adivce here (https://forums.autodesk.com/t5/revit-api/call-class-from-windows-form/m-p/5984297/highlight/true#M13... I am passing the ExternalCommandData through a called Form, and then again to the called class. The called class is being run as a backgroundWorker, and in it when I try to access the UIApplication, Revit crashes.  As I step through the code, whenever I try to inspect the UIApplication it results in a crash.  The ExternalCommandData looks to pass fine, I just can't get a UIApplication out of it.

 

This is the code to call the Form. The ExternalCommandData is commandData.

 

            using (Form1 thisForm = new Form1(path, commandData))
            {
                thisForm.ShowDialog();
                if (thisForm.DialogResult == System.Windows.Forms.DialogResult.Cancel)
                {
                    return Result.Cancelled;
                }
                if (thisForm.DialogResult == System.Windows.Forms.DialogResult.OK)
                {
                    path = thisForm.WordDocPath;
                }
            }

This is the begining of the code to call the class inside the Form

 

 

    public class ReplaceImages
    {
private void backgroundWorker2_Dowork(object sender, DoWorkEventArgs e) { int pageCount = (int)e.Argument; e.Result = replace1.replaceImages(commandData, WordDocPath, pageCount); }

 

And then inside the actual class to replace the images

 

    public class ReplaceImages
    {
        public delegate void ProgressUpdate(int value);
        public event ProgressUpdate OnProgressUpdate;
        public ExternalCommandData commandData;


        public Result replaceImages(ExternalCommandData commandDatax, string WordDocPath, int pageCount)
        {
            commandData = commandDatax;

            UIApplication uiApp = commandData.Application;
            UIDocument uidoc = uiApp.ActiveUIDocument;
            Application app = uiApp.Application;
            Document doc = uidoc.Document;

            FilteredElementCollector col = new FilteredElementCollector(uiApp.ActiveUIDocument.Document).OfCategory(BuiltInCategory.OST_RasterImages);

            string imagePath = Path.GetDirectoryName(WordDocPath);
            imagePath = imagePath + @"\Sheet Spec (Images)\";

            int imageNumber = 1;
            foreach (Element e in col)

Any Idea why I am unable to get a functioning UIApplication in the ReplaceImage class?

0 Likes
Accepted solutions (1)
2,532 Views
4 Replies
Replies (4)
Message 2 of 5

arnostlobel
Alumni
Alumni
Accepted solution

The problem is not with the data class; the problem is with calling the API from an outside worker thread, which is not allowed. All calls into the Revit API must be done on the main thread as a direct respond to Revit's invocation into an external add-in (command, event, macro, etc.) If you need to execute an API call from a worker thread (or a modeless dialog, the same thing essentially), you have to utilize either the Idling event or an ExternalEvent. Examples of both are provided in the SDK under the ModelessDialog folder. (Searching this forum or the web ought to also yield useful information on calling Revit from outside threads.)

 

By the way, while on the topic, there is almost never a need to pass an antire instance of ExternalCommmadData from an external command's Execute method into another method. In most cases, it is either just the Application object or the active UIDocument or the Document of the active document that the other method actually needs. 

Arnošt Löbel
Message 3 of 5

Anonymous
Not applicable
Arnott, thanks for the information. That makes sense. I will look into Idling or ExternalEvent and see which is more appropriate to use. I did try to pass just the Application, but It was failing. I guess it was because it was as a background worker which isn't allowed.
0 Likes
Message 4 of 5

lariasGHNFM
Contributor
Contributor

Question,

Say I have an external Application and an External Command.

I want to pass along the UIDocument and UIApplication 

to another class that I added to my project. 

How can I reference or make sure my new class is using the same  UIDocument and UIApplication ? With my little knowledge of C# and Revit api, I know that we get access to the uidoc and uiapp via the interfaces that we implement but how do We give a new  form or class that same access ?

 

Luis. 

0 Likes
Message 5 of 5

jeremy_tammik
Alumni
Alumni

Simple answer:

  

how do We give a new form or class that same access?

  

Basically, you do not.

  

The Revit API is almost 100% event-driven.

  

The API calls can only be made within a valid Revit API context, and such a context is only given inside one of the officially provided event handlers, and every event handler is provided with the required document and application access when it is called by Revit.

  

Note: the most common event handler is the external command Execute method.

  

Jeremy Tammik Developer Advocacy and Support + The Building Coder + Autodesk Developer Network + ADN Open
0 Likes