I have a PushButton in a revit API ribbon and would like to simulate a press on it in order to do some tests (I need a ExternalCommandData object from the currently active document). However I cannot seem to find anything like a PushButton.Click() function.
var panel = Application.CreateRibbonPanel("a", "b")
var buttonData = new PushButtonData(name, name, ApplicationInfo.AddInPath, "TestZone.Commands." + "DefaultCommand");
var button = panel.AddItem(buttonData) as PushButton;
With Application being of course the default UIControlledApplication on the OnStartup function. Anyway to know simulate a button click so that I can obtain an ExternalCommandData object of the currently opened document (In the final version there will be checks to ensure that a document is already open ext.) Or is there another way to obtain an externalCommandData?
Note that this question may require you to know the revit API, I doubt that just knowledge of c# will be enough to answer this.
I had many of the same issues with unit testing Revit - and the other users are right, there is no way to get an ExternalCommandData object without running a command. Fortunately, there's a framework that makes a lot of this possible by automating the startup and running of Revit externally.
https://github.com/DynamoDS/RevitTestFramework
The Dynamo group built this framework to automate their tests, and it offers a lot of great functionality.
Most pertinently for you, it actually exposes a valid ExternalCommandData object
Here is some example code from their framework.
/// <summary>
/// Using the TestModel parameter, you can specify a Revit model
/// to be opened prior to executing the test. The model path specified
/// in this attribute is relative to the working directory.
/// </summary>
[Test]
[TestModel(#"./bricks.rfa")]
public void ModelHasTheCorrectNumberOfBricks()
{
var doc = RevitTestExecutive.CommandData.Application.ActiveUIDocument.Document;
var fec = new FilteredElementCollector(doc);
fec.OfClass(typeof(FamilyInstance));
var bricks = fec.ToElements()
.Cast<FamilyInstance>()
.Where(fi => fi.Symbol.Family.Name == "brick");
Assert.AreEqual(bricks.Count(), 4);
}
RevitTestExecutive.CommandData offers the ExternalCommandData you are looking for.
Note that there's an issue with installing the RTF as an admin on your machine. I recommend installing it to a local directory as a local user so you don't run into Windows UAC issues.
There is no way to obtain an ExternalCommandData object without running a command.
You may be able to use UI Automation to programmatically click on your PushButton. It may be simpler to configure a keyboard shortcut for your command and use UI Automation to simulate the keypresses for you.
See Jeremy Tammik's blog for information on using UI Automation in Revit: http://thebuildingcoder.typepad.com/blog/automation/
You cannot simulate the click to launch an external command, and you cannot create a valid ExternalCommandData object yourself. Only Revit can do that when calling your event handler and providing it with a valid Revit API context in a call-back function, i.e., Revit event handler such as the external command Execute method.
You can however launch an external command from some other valid Revit API context, just as you can a built-in Revit command, using the PostCommand API:
http://thebuildingcoder.typepad.com/blog/about-the-author.html#5.3
Related
I'm looking for some help. I am in need of figuring out how to get an object reference to Microsoft Access 2013 so that I can (through Automation) call some of the already defined functions in the accdb. For instance; I want to automate the process of "RelinkODBCTables" function which repoints the linked tables to another data source from my .net core 3.0 c# application.
I've not been able to successfully get a reference to interop but I may not be doing it correctly.
Any help would be greatly appreciated.
D-
If you want to
Create a instnce of Access.
Call a VBA sub (say your relink code).
Close the the database.
quit access.
You could use this code:
{
object AccessApp;
AccessApp = Interaction.CreateObject("Access.Application");
AccessApp.OpenCurrentDatabase(#"c:\test\test44.accdb");
AccessApp.Run("MyLinker");
AccessApp.CloseCurrentDatabase();
AccessApp.Quit();
}
So, you don't need any referance at all. Just create a instance of the given applicatin (word, Excel or as per above Access).
At that point, you have full use of the object model, and can use run to call some VBA routine. In above, we call a VBA Sub called MyRelinker.
About the only caution here is that when you create the instance of that object, then all startup code of the application will run. So, if on startup the forms and UI that the developer of the Access program launches any prompt, then you can't "answer" that prompt. So, how well this works will VERY much depend on how nice the application plays on startup, and that calling any of those VBA routines does not trigger some kind of prompt(s) in that Access application. If it does, then you in trouble, since you can't "answer" any of the forms or code prompts that access may very well throw up at the end user.
And, if you do want some "inteli-sense" during coding, then you can add a office "interop" reference to your project. Its not required, but if you not really fluent in the Access VBA + object model, then in place of CreateObject("Access.Application"), if you do referance the office assemby, say this one:
Microsoft.Office.Interop.Access
C:\Program Files (x86)\Microsoft Visual Studio 12.0\
Visual Studio Tools for Office\PIA\Office14\
Microsoft.Office.Interop.Access.dll
Then you code becomes this:
{
Microsoft.Office.Interop.Access.Application AccessApp =
new Microsoft.Office.Interop.Access.Application();
AccessApp.OpenCurrentDatabase(#"c:\test\test44.accdb");
AccessApp.Run("MyLinker");
AccessApp.CloseCurrentDatabase();
AccessApp.Quit();
}
However, while you get stronger typing with the reference, you often will "tie" you code to a given version of Access, and a simple use of CreateObject() quite much means that you can create a instance of Any Access installed on the target computer, and it should work going back all the way to office 2000 - a good 20 years of "coverage".
Keep in mind that you CAN NOT create a instance of the Access runtime, the target computer will requite a full version to "create" a instance of the Access.Application object.
You "can" automate the runtime version. This involves launching Access runtime (via Shell()), and then grabbing an instance with "GetObject()" in place of CreateObject().
Edit
I should point out that in the 2nd above code example, and using the office interop-assembly reference, I choose office 14, which is office 2010. In your case, you have to use office 15 (2013).
I have built a WinForms application which consists of just a RichTextBox and ive built it into an EXE.
The purpose of this EXE is to act as a logger so that when im running tests the log is output onto this EXE.
Important note is that this EXE is referenced in a separate Unit Test (to be run by Microsoft Test Manager) solution by adding the EXE as a reference in the project. This seems to expose the objects I need.
I've had some success using HTTP through the ChannelFactory interfaces, but i'd prefer to talk to the EXE directly.
These unit tests I have are essentially sending and receiving data from OpenVMS, and some of these tests can take some time to complete.
So I built a new Console project to test the public methods I've exposed in my logger.exe and so far heres my code:
TerminalLogger.Logger term = new TerminalLogger.Logger();
term.TerminalLog("Test");
When I run this the form opens, but nothing loads in. So im assuming thats because the form runs on the same thread? Do I need to have this form running on its own thread?
I notice that if I add "term" to watch in Visual Studio and inspect the richtextbox I can see that it has actually written the value "Test" to the object, but of course I cannot see this as the form hasnt fully rendered in.
I still need to be able to call methods like term.LogMessage("Example Message") and get it to display on the form.
If you need extra info please add a comment and i'll do my best to elaborate more on my question.
Well I asked one of the web developers at my organisation on the offchance they might be able to help, and apparently calling a redraw fixed my issue!.
I simply added:
LogConsole.TextChanged += (sender, e) =>
{
if (this.Visible)
{
this.Refresh();
}
};
Here is how I call the code from my unit test:
TerminalLogger.Logger term = new TerminalLogger.Logger();
term.Show();
term.TerminalLog("Test1");
term.TerminalLog("Test2");
And I can send messages to it without it locking up.
I recently wrote an Outlook plug-in (for use in Outlook 2010) for a client that syncs up their user's Outlook databases with a third-party application. This works fine. However, one thing that the client wants to do is to leverage standard copy/paste functionality to duplicate records in Outlook. The issue is that I define a UserProperty for any Outlook record that has been sent to the third-party system (it stores the internal ID from the third-party system). When the client performs a copy/paste, this UserProperty is also copied (which is bad as it creates multiple records with the same third-party ID). I was wondering if there was some way to detect via code that a copy/paste was occurring and to make a modification in the record (removal of this UserProperty) prior to save. As they might perform this on Tasks, Appointments, or Contacts, I would need guidance that would apply across the board.
Per the suggestions below, I attempted to leverage BeforeItemPaste, as seen below:
private void ThisAddIn_Startup(object sender, System.EventArgs e) {
Globals.ThisAddIn.Application.ActiveExplorer().BeforeItemPaste += new Outlook.ExplorerEvents_10_BeforeItemPasteEventHandler(Item_BeforeItemPaste);
}
private void Item_BeforeItemPaste(ref System.Object ClipboardContent,
Microsoft.Office.Interop.Outlook.MAPIFolder Target, ref bool Cancel) {
System.Windows.Forms.MessageBox.Show("Trying to paste");
}
The message appears the very first time I do a copy/paste, and then never again. I tried leveraging Application.Explorers, which theoretically gives me access to all Explorers in Outlook, but that didn't give me access to the individual ones (Appointment, Task, Contact) that I was looking for (there was only one element in that 'array' when I debugged it). Also, I tried to access those specific Explorer items by invoking MAPIFolder.GetExplorer, but this always returned null.
Try to use Explorer.BeforeItemPaste event.
i'd like to do the following :
whenever a word document is open i need to save it in a way, and then if a user starts typing in it i want to save the time the document is being edited.
i'm just on the first phase, and i can't seem to manage detecting when a user opens a document.
i tried using Microsoft.Office.Interop.Word, but, in this way i don't want to start word application unless the user opens a document. but, when i want to initialize a Microsoft.Office.Interop.Word.Application, it's the only way i saw possible.
is there a way, by using the Microsoft.Office.Interop.Word API to detect event of opening a file by a user ?
i tried the following (obviously it doesn't work, since it's just opens a word office application)
using Word = Microsoft.Office.Interop.Word;
Word.Application oWord = new Word.Application();
oWord.Visible = true;
oWord.DocumentChange += new Word.ApplicationEvents4_DocumentChangeEventHandler(oWord_DocumentChange);
...
private void oWord_DocumentChange()
{
Console.WriteLine("DocumentChange");
}
also, i wanted to maybe use Microsoft.Office.Interop.Word.Document, but couldn't.
i started developing a method of my own, but its just seems to be a waste since this api is already build.
any help will be great.. thanks.
Have you already tried creating an Application level Add-in. That add-in should have all the event handlers you need to detect the first and last change to the document.
Maybe you could keep checking for open instances of Word every so often, and if you find one, then you use interop to get that instance.
You could probably use something like FindWindow or EnumWindows to check for instances of Word (or there might be some built in way of doing that in .Net that I can't remember right now), and then perhaps use GetObject to get the instance. This link describes GetObject vs CreateObject.
I am using the Windows API Code Pack for Microsoft .NET Framework to try out of some of the new UI features of the Win7 taskbar. I am coding in C#.
I have a question regarding jumplists. All of the sample code provided assumes that the entries on the jump list are used to call out to run a particular application or open a document, e.g. a text document in a MRU list or run mspaint.exe.
I would like to implement some items which allow me to set state in my own application (i.e. the app which is interacting with the taskbar). MSN Messenger does this, for example, when you can set your status (Busy, Offline etc.).
Try as I might, I cannot create a JUmpListItem or JumpListLink to behave in this way - it treats them as applications or documents.
Does anyone have any samples of how to create an item which raises an event in the same application that created it? I am sure it is simple but I am being very daft.
Many thanks for your help.
I believe what you'd want to do is to call your application with a special set of flags (i.e. launch the executable with certain arguments). At application start up, you'd check to see what flags are set, then send a message to the main instance of the application and then exit out of the new instance.
Using the TaskBarDemo, to open an item created by your application would have to be referenced, ie if your program created a PDF file you would do this:
jumpList.AddUserTasks(new JumpListLink(Path.Combine(systemFolder, "C:\\Program Files\\Adobe\\Reader 9.0\\Reader\\AcroRD32.exe"), "Open Adobe Reader")
{
IconReference = new IconReference(Path.Combine(systemFolder, "C:\\Program Files\\Adobe\\Reader 9.0\\Reader\\AcroRD32.exe"), 0)
});
Otherwise you would have to ensure that your application registered file associations, for recent or frequent items.
I had a few problems with jumplists with the API Pack, i now use VS 2010 Beta 2 and let shell handle the jumplists.
Hope this is helpfull.
These tasks are some sort of IShellLink. Then, you should call ICustomDestinationList's AddUserTasks. Look up samples in Windows 7 Training Kit.