Thursday, February 10, 2011

Status Bar Text and WPF

Yesterday, Jeremy Tammik posted how to change the status bar text in Revit. Toward the end of his post, he mentions using GetCurrentProcess to find the HWND of the main Revit window.

Yay! says I: Now I can print out status text, without cluttering up my shiny WPF window.

Nay! says WPF: It doesn't work.

Imagine my frustration... I spent some time analysing this using SPY++ and found out, that WPF dialogs are not children of the main application window. If your IExternalCommand starts a WPF window, then you should note the HWND first and then show the dialog. You can keep the HWND in a class variable, since it won't change. I ended up with a utility static class StatusText:

public static class StatusText
    [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);

    private static IntPtr _hwndRevit = new IntPtr();

    /// <summary>
    /// Set the handle to the revit main window here before calling
    /// SetStatusText.
    /// (also, set it in the IExternalCommand.Execute thread, not
    /// in your WPF thread)
    /// </summary>
    public static void SetRevitWindowHandle(IntPtr hwnd)
        _hwndRevit = hwnd;

    /// <summary>
    /// Set the status text of the current revit instance.
    /// </summary>
    public static void SetStatusText(string text)
        IntPtr statusBar = FindWindowEx(_hwndRevit, IntPtr.Zero,
                                        "msctls_statusbar32", "");

        if (statusBar != IntPtr.Zero)
            SetWindowText(statusBar, text);


You can then just issue a

StatusText.SetStatusText("hello, world!");

whenever you need to display something cool. Bonus points for hooking this up to a TraceListener ;)