diff --git a/src/toolkit/Community.VisualStudio.Toolkit.Shared/Windows/ToolkitToolWindowPane.cs b/src/toolkit/Community.VisualStudio.Toolkit.Shared/Windows/ToolkitToolWindowPane.cs index eea8866..84d0239 100644 --- a/src/toolkit/Community.VisualStudio.Toolkit.Shared/Windows/ToolkitToolWindowPane.cs +++ b/src/toolkit/Community.VisualStudio.Toolkit.Shared/Windows/ToolkitToolWindowPane.cs @@ -1,5 +1,6 @@ using System; using Microsoft.VisualStudio.Shell; +using Microsoft.VisualStudio.Shell.Interop; namespace Community.VisualStudio.Toolkit { @@ -10,6 +11,8 @@ namespace Community.VisualStudio.Toolkit public abstract class ToolkitToolWindowPane : ToolWindowPane { private bool _isInitialized; + private WindowFrame? _windowFrame; + private bool _isWindowFrameAvailable; /// protected override void Initialize() @@ -22,5 +25,72 @@ protected override void Initialize() internal bool IsInitialized => _isInitialized; internal event EventHandler? Initialized; + + /// + public override void OnToolWindowCreated() + { + base.OnToolWindowCreated(); + _isWindowFrameAvailable = true; + WindowFrameAvailable?.Invoke(this, EventArgs.Empty); + } + + /// + /// Indicates whether the method can be called. + /// + public bool IsWindowFrameAvailable => _isWindowFrameAvailable; + + /// + /// Raised when Visual Studio creates the tool window's frame. + /// The property can be accessed from this point onwards. + /// + public event EventHandler? WindowFrameAvailable; + + /// + /// Gets the tool window's window frame. + /// + /// This method can only be called after Visual Studio has created the window frame. + /// You can detect this in various ways: + /// + /// + /// Override the method. + /// When this method is called, the window frame will be available. + /// + /// + /// Listen for the event. + /// When the event is raised, the window frame will be available. + /// + /// + /// Check the property. + /// + /// + /// + /// + /// The window frame is not available. + protected WindowFrame GetWindowFrame() + { + if (_windowFrame is null) + { + // The `Frame` property has to be set by Visual Studio, so it might + // be null at this point. It's also typed as an `object` even though + // internally it's stored as an `IVsWindowFrame`, so we can use + // type matching to both cast and confirm that it's not null. + if (Frame is IVsWindowFrame vsWindowFrame) + { + // We could create the WindowFrame in `OnToolWindowCreated`, + // but we delay-create it for two reasons: + // 1. It may not ever be needed. + // 2. If a derived class also overrides `OnToolWindowCreated`, then the window + // frame would only be available after it called `base.OnToolWindowCreated()`. + // Delay-creating it means that it will be available before that call is made. + _windowFrame = new WindowFrame(vsWindowFrame); + } + else + { + throw new InvalidOperationException("The tool window's frame is not available yet."); + } + } + + return _windowFrame; + } } }