Hi,
I wonder how can I load a revit file automatically when start the revit system.
Since the UIControlledApplication is the only parameter of the OnStartup function of the IExternalApplication class, while the OpenAndActivateDocument is the function of the UIApplication but not of the UIControlledApplication.
How can I invoke the UIApplication. OpenAndActivateDocument in OnStartup function? or is there other methods to address the problem?
Thx !
Solved! Go to Solution.
Solved by jeremytammik. Go to Solution.
Solved by arnostlobel. Go to Solution.
Dear Davix,
The easiest way to start up Revit with a project file loaded is to use Process.Start:
http://thebuildingcoder.typepad.com/blog/2010/03/using-processstart-to-open-a-project-or-family.html
Once Revit is up and running, you can also use the UIApplication.OpenAndActivateDocument method:
http://thebuildingcoder.typepad.com/blog/2012/12/closing-the-active-document.html
You probably cannot do anything at all like this in the OnStartup method, because Revit is not yet fully started up and ready to process documents when that method is called.
You have to wait until later, for instance by temporarily subscribing to the Idling event in the OnStartup method, then unsubscribing from Idling in the Idling event handler, opening the document and doing whatever else you want at that point.
Cheers,
Jeremy
Dear Davix,
I expanded a bit on my initial answer and published a summary of this thread on The Building Coder:
You might want to check out the enhanced answer there as well.
Thank you!
Cheers,
Jeremy
Davix, And Jeremy:
Using the Idling event is not anymore the only solution for the problem as stated by Davix. Idling event might be the only work-around three years ago, but there is a better solution now. Since R2015 (if I recall correctly), a better way of opening a startup document is via the ApplicationInitialized event. Like with the Idling event, an application subscribes to it during the OnStartup call. Revit will then raise this event a bit later when the Revit application’s start-up has been completed. Remember, just with the Idling event, the OpenAndActivateDocument can be used only as long there is no active document in Revit yet.
Dear Jeremy,
Thank you very much for your prompt and detail solution.
However, when I?test the?System.Diagnostics.Process.Start mechod, the revit file can be openned when start the Revit but disappear only straight away.
And I don't know how to test the idle event and how to?use the UIApplication.OpenAndActivateDocument method in onstart() since can not get the?UIApplication in the onstart().
Thx & Regards,
Davix
Dear Jeremy and Arnošt Löbel,
When I used the event ApplicationInitialized and the System.Diagnostics.Process.Start in the event handler, it works now.
Thx for ur kindness direction !!
Regards,
Davix
Arnost,
Thanks for jumping in on the discussion - it's one I've had an ongoing interest in.
I'm not sure if I understand your suggestion to use the ApplicationInitialized event. While I agree that event is the right one to use for the timing of when it executes, it doesn't seem like we have access to the right contexts at the right time?
- If we register it during startup, we're registering it against the ControlledApplication class.
- When we are in the callback after, we're in the callback of the ControlledApplication class.
When we're in that callback - how would we have access to the UIApplication object so that we could call OpenAndActivate()?
Thanks,
Matt
Hello Matt,
I may be wrong, but I think it works differently than what you guessed. The object inside the OnStartup is indeed a ControlledApplication, but it ought to be an Application instance as the sender argument of the ApplicationInitialized even handler. With that you will be able to construct an instance of UIApplication and use its methods. (Unfortunately, most of us New Englanders are working from their homes today due to a storm, and I am trying to limit my debugging sessions to a minimum! If it turns out it is not working for you as I suggested, however, Ilet me know and ’ll debug it myself by tomorrow or Wednesday.)
Just as a side note: it does not matter whether an event handler subscribes to an event using ControlledApplication or Application. The repository of application event delegates are always with the Application instance (internally).
Dear Arnošt,
You make it sound so easy...
Here we are, an hour or two later:
class App : IExternalApplication { const string _test_project_filepath = "Z:/a/rvt/CurvedWall.rvt"; UIApplication _uiapp; public Result OnStartup( UIControlledApplication a ) { // These attempts to access a UIApplication // or Application instance are all in vain: // //_uiapp = (UIApplication) a; //_uiapp = (UIApplication) a.ControlledApplication; //Application app = (Application) a; //Application app2 = (Application) a.ControlledApplication; //Application app3 = a.m_application; // Using Reflection works, though: Type type = a.GetType(); // Not useful in this case, but interesting: MemberInfo[] publicMembers = type.GetMembers(); MemberInfo[] nonPublicMembers = type.GetMembers( BindingFlags.NonPublic ); MemberInfo[] staticMembers = type.GetMembers( BindingFlags.Static ); // This is the call that finally yields useful results: BindingFlags flags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.GetProperty | BindingFlags.Instance; MemberInfo[] propertyMembers = type.GetMembers( flags ); // Note that the field "m_application" is listed // in the propertyMembers array, and also the // method "getUIApp"... let's grab the field: string propertyName = "m_application"; flags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.GetField | BindingFlags.Instance; Binder binder = null; object[] args = null; object result = type.InvokeMember( propertyName, flags, binder, a, args ); _uiapp = (UIApplication) result; a.ControlledApplication.ApplicationInitialized += OnApplicationInitialized; return Result.Succeeded; } void OnApplicationInitialized( object sender, ApplicationInitializedEventArgs e ) { UIApplication uiapp = sender as UIApplication; Application app = sender as Application; // uiapp is null, so this call will fail: if( null != uiapp ) { // We never enter here... uiapp.OpenAndActivateDocument( _test_project_filepath ); } // This call succeeds, though: _uiapp.OpenAndActivateDocument( _test_project_filepath ); } public Result OnShutdown( UIControlledApplication a ) { return Result.Succeeded; } }
It works!
Is that what you intended?
🙂
Cheers,
Jeremy
Arnost -
Thanks! you are correct, the sender of the ApplicationInitialized event callback is the Application (not the controlled application). So my quest of a non-hacky way to transition between the ControlledApplication and the Application is finally solved!
As a fellow Bostonian, I'm also snowed in today. But if you want your sidewalk cleared, let me know - I am in your debt! 🙂
Jeremy - I'm not sure that the reflection was actually necessary? was that just exploration?
Best Regards,
Matt
Jeremy,
Perhaps I made it sound simple because it is in fact rather simple. Indeed, as Matt already noted, no reflection is necessary. In the OnStartup you get UIControlledApplication, from which you can get ControlledApplication (the latter always exists if the former does,) and on that you subscribe to the ApplicationInitialized. Keep in mind you cannot cast a ControlledApplicaiton to Application – that is the ControlledApplicaiton’s very point of existence.
Then in the event handler the sender should be an instance of Application, which can be used to instantiate an UIApplication, on which the OpenAndActivateDocument method can be invoked. Like I said, quite simple. 😉 Each of the two methods is only a few lines of code.
Matt, it is trully a generous offer about you shoveling my sidewalk, but I sugest you sleep on it before we confirm for sure. There is a lot more snow still coming 🙂
Dear Arnošt and Matt,
Oh dear. What a waste.
Yes, of course, given the UIApplication constructor taking an Application argument, all is clear and the Reflection is not needed. I did not notice that, and what I described was the first alternative I could find 🙂
Well, well, apparently all roads lead to Rome after all... but it saves effort taking the easiest path.
I wish you both lots of fun shovelling!
Thank you!
Cheers,
Jeremy
For the sake of completeness and those that trust nothing but source code, here is the complete solution without using Reflection:
class App : IExternalApplication { const string _test_project_filepath = "Z:/a/rvt/CurvedWall.rvt"; public Result OnStartup( UIControlledApplication a ) { a.ControlledApplication.ApplicationInitialized += OnApplicationInitialized; return Result.Succeeded; } void OnApplicationInitialized( object sender, ApplicationInitializedEventArgs e ) { // This does not work, because the sender is // an Application instance, not UIApplication. //UIApplication uiapp = sender as UIApplication; // Sender is an Application instance: Application app = sender as Application; // However, UIApplication can be // instantiated from Application. UIApplication uiapp = new UIApplication( app ); uiapp.OpenAndActivateDocument( _test_project_filepath ); } public Result OnShutdown( UIControlledApplication a ) { return Result.Succeeded; } }
Cheers,
Jeremy
That'll do, Jeremy. Thanks for summing it up.
It could also be noted, for the sake of completness, that it is the majority of application events of which the sender is the Application object. There is only a handful (if at all that much) of events which are raised by the UIApplication. Those are more like an exceptions to the rule, as documented in the SDK (the chm file.)
Dear Jeremy & Mattmason & Arnošt Löbel,
Thank you for your interests in the question I raised.
In fact, followed Jeremy & Arnošt Löbel' suggestion, I solved it like this, :
-------------------------------------------------------------------------------------------
public Result OnStartup(UIControlledApplication application) {
try
{
// Register event.
application.ControlledApplication.ApplicationInitialized += new EventHandler
<Autodesk.Revit.DB.Events.ApplicationInitializedEventArgs>(TestAutomation);
}
catch (Exception)
{
return Result.Failed;
}
return Result.Succeeded;
}
public Result OnShutdown(UIControlledApplication application) {
pplication.ControlledApplication.ApplicationInitialized -= new EventHandler
<Autodesk.Revit.DB.Events.ApplicationInitializedEventArgs>(TestAutomation);
return Result.Succeeded;
}
public void TestAutomation(object sender, ApplicationInitializedEventArgs args)
{
System.Diagnostics.Process.Start(
@"C:\BIMSamples\RoomsRenumbering\RoomsRenumbering\roomsRenumbering.rvt");
}
------------------------------------------------------------------------------------
Still, I cannot use the UIApplication in the codes aboved. And I'll try Jeremy's code latter.
Thx again for your attention and kind suggestion!
Regards,
Davix
Can't find what you're looking for? Ask the community or share your knowledge.