Status Bar Text
Originally Published inThe ski tour season in the alps has opened again, and I went on my first tour of the year last weekend, to the Ramoz hut and the Arosa Rothorn:
Many thanks to Chris for the beautiful pictures ↗!
Here is another idea from Rudolf Honke of acadGraph CADstudio GmbH ↗. He says:
I previously explained how you can use UIAutomation event handlers in Revit.
When playing around a bit further with this, I thought about how to display the events.
Using good old P/Invoke, you can simply display any text in the Revit status bar (http://www.pinvoke.net ↗ helps a lot):
[DllImport( "user32.dll",
SetLastError = true,
CharSet = CharSet.Auto )]
static extern int SetWindowText(
IntPtr hWnd,
string lpString );
[DllImport( "user32.dll",
SetLastError = true )]
static extern IntPtr FindWindowEx(
IntPtr hwndParent,
IntPtr hwndChildAfter,
string lpszClass,
string lpszWindow );
public static void SetStatusText( string text )
{
IntPtr statusBar = FindWindowEx(
m_mainWndFromHandle, IntPtr.Zero,
"msctls_statusbar32", "" );
if( statusBar != IntPtr.Zero )
{
SetWindowText( statusBar, text );
}
}
This is a comfortable way to show the events being fired. Here is an example of resizing the main Revit window:
Resizing the main Revit window again:
Selecting the Home ribbon tab:
Selecting the Insert ribbon tab:
Selecting a button on the Annotation ribbon tab:
One point to keep in mind is that Revit will overwrite your text as soon as it sees fit, which may be within a few milliseconds, depending on the command you invoked.
Here is another button being selected and the corresponding UI Automation event displayed:
In this case, it is immediately overwritten by Revit:
Actually, this function could be combined with another function to replace the global variable ‘m_mainWndFromHandle’:
public static void SetStatusText( string text )
{
IntPtr mainWindowHandle = IntPtr.Zero;
Process[] processes
= Process.GetProcessesByName( "Revit" );
if( 0 < processes.Length )
{
mainWindowHandle
= processes[0].MainWindowHandle;
IntPtr statusBar = FindWindowEx(
mainWindowHandle, IntPtr.Zero,
"msctls_statusbar32", "" );
if( statusBar != IntPtr.Zero )
{
SetWindowText( statusBar, text );
}
}
}
So, this function could be called without filling the global Revit app handle before. Or even shorter:
public static void SetStatusText( string text )
{
Process[] processes
= Process.GetProcessesByName( "Revit" );
if( 0 < processes.Length )
{
IntPtr statusBar = FindWindowEx(
processes[0].MainWindowHandle, IntPtr.Zero,
"msctls_statusbar32", "" );
if( statusBar != IntPtr.Zero )
{
SetWindowText( statusBar, text );
}
}
}
Think of a situation there more than one instance of Revit is running, RAC and MEP, for example. Using the original code, the wrong window might be addressed.
Jeremy adds: I went ahead and implemented a minimal new external command CmdStatusBar for The Building Coder samples to demonstrate this. I actually make use of the GetCurrentProcess method instead, since I am inside the Revit process, like this:
public static void SetStatusText(
IntPtr mainWindow,
string text )
{
IntPtr statusBar = FindWindowEx(
mainWindow, IntPtr.Zero,
"msctls_statusbar32", "" );
if( statusBar != IntPtr.Zero )
{
SetWindowText( statusBar, text );
}
}
public Result Execute(
ExternalCommandData commandData,
ref string message,
ElementSet elements )
{
IntPtr revitHandle = System.Diagnostics.Process
.GetCurrentProcess().MainWindowHandle;
SetStatusText( revitHandle, "Kilroy was here." );
return Result.Succeeded;
}
It works fine. Here is version 2011.0.83.0 of The Building Coder samples including the complete source code and Visual Studio solution with the new command.