Tool Window is closing ... Or is it really

Wow! That was a pretty long vacation for the good doctor. I know that most of you have been aching to hear from me. Anyway I missed you all a lot and am glad to be back. Hope you and your family had nice Halloween! It was nice for us.

            I don't know about you but I hate it when I am not notified of power being down especially this time of the year Halloween. That is exactly what happened couple of years ago. The power went out in our neighborhood and with Trick or Treating kids everywhere. So rather than having fun kids got scared themselves. A little bit of advance notification would have meant a better Halloween experience for those kids.

            Ok you must be wondering why he is rambling on all this irrelevant details. Wouldn’t you like to get notified when your tool window does a slide or hides or docks or closes? If you are like me you surely would like to.

            Fact of the matter is tool windows really don’t ever close. They always hide when you close them via the UI. Document windows however do get closed when you close them. I did not realize this subtle detail until I played around with it. So what you need is a view helper object that implements IVsWindowFrameNotify. A VSPackage that needs to optimize or have extensive control over windows sited in window frames implements this interface. One reason for this might be to control the number of window handles. Set the __VSFPROPID.VSFPROPID_ViewHelper property of the window pane to an object that implements IVsWindowFrameNotify. Another thing to note is that the IVsWindowPane that your tool window implements does not get these notifications. The only notification that you can possibly get is the IVsWindowPane::Close call however for single instance tool windows that is called only when the frame window is tearing down due to IDE shutdown. For multi instanced tool windows IVsWindowPane::Close does get called when you Close the tool window as opposed to Hide it. For all other window state changes your IVsWindowFrameNotify::OnShow gets called. You can inspect the fShow parameter that gets passed in to see what kind of event you are called for and do the needful. If your tool window is displayed as a tabbed document then you can also implement IVsWindowFrameNotify2::OnClose which gets called when the window frame closes. Below is a code sample that shows you how to do this in managed packages. One caveat though, for OnClose to get called you also have to implement IVsWindowFrameNotify.

Tracking Tool Window Frame events

public class WindowFrameSink :

Microsoft.VisualStudio.Shell.Interop.IVsWindowFrameNotify

{

       int IVsWindowFrameNotify.OnDockableChange(int fDockable)

       {

              return NativeMethods.S_OK;

       }

     

       int IVsWindowFrameNotify.OnMove()

       {

              return NativeMethods.S_OK;

       }

       int IVsWindowFrameNotify.OnShow(int fShow)

       {

              switch (fShow)

              {

                     case (int) __FRAMESHOW.FRAMESHOW_AutoHideSlideBegin:

                           Debug.WriteLine("FRAMESHOW_AutoHideSlideBegin");

                           break;

                     case (int) __FRAMESHOW.FRAMESHOW_WinClosed:

                           Debug.WriteLine("FRAMESHOW_WinClosed");

                           break;

                     case (int) __FRAMESHOW.FRAMESHOW_WinShown:

                           Debug.WriteLine("FRAMESHOW_WinShown");

                           break;

                     case (int) __FRAMESHOW.FRAMESHOW_WinHidden:

                           Debug.WriteLine("FRAMESHOW_WinHidden");

                           break;

                     default:

                           break;

              }

       return NativeMethods.S_OK;

       }

       int IVsWindowFrameNotify.OnSize()

       {

              return NativeMethods.S_OK;

       }

}

 

Now you would be tempted to set up the ViewHelper object for the window frame as below:

Using the sink object directly

sink = new WindowFrameSink();

NativeMethods.ThrowOnFailure(windowFrame.SetProperty(

                  (int) __VSFPROPID.VSFPROPID_ViewHelper, (object)sink));

You would realize that it still isn't working. Well that is because the Interop definitions for the second paramter is marshaled as a VARIANT however the type is set to VT_DISPATCH instead of VT_UNKNOWN. The shell explicitly asks for VT_UNKNOWN. So all you need to do is wrap this object with a System.Runtime.InteropServices.UnknownWrapper instance. Correct code will be something as below:

Using the sink object directly

sink = new WindowFrameSink();

UnknownWrapper punk = new UnknownWrapper(sink);

NativeMethods.ThrowOnFailure(windowFrame.SetProperty(

                  (int) __VSFPROPID.VSFPROPID_ViewHelper, (object)punk));

Same technique can be utilized for Document windows. In Visual Studio 2005 you could use IVsWindowFrame2 interface and call the Advise method to pass in your IVsWindowFrameNotify implementation to be called back on. Newer window frames implement this interface.