From ae290851c6604ad67822b660fdde3eda7b6eef17 Mon Sep 17 00:00:00 2001 From: reduckted Date: Thu, 16 Feb 2023 22:07:11 +1000 Subject: [PATCH 1/2] Added the ability to get a WindowFrame wrapper for a tool window's frame. --- .../Windows/ToolkitToolWindowPane.cs | 70 +++++++++++++++++++ 1 file changed, 70 insertions(+) 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; + } } } From 63fc5c347f685a52ec1b3fbc7cb36c9228c8ee1e Mon Sep 17 00:00:00 2001 From: reduckted Date: Thu, 16 Feb 2023 22:07:43 +1000 Subject: [PATCH 2/2] Added an example of using the tool window's GetWindowFrame() method. --- demo/VSSDK.TestExtension/ToolWindows/RunnerWindow.cs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/demo/VSSDK.TestExtension/ToolWindows/RunnerWindow.cs b/demo/VSSDK.TestExtension/ToolWindows/RunnerWindow.cs index 8bcd501..13348a6 100644 --- a/demo/VSSDK.TestExtension/ToolWindows/RunnerWindow.cs +++ b/demo/VSSDK.TestExtension/ToolWindows/RunnerWindow.cs @@ -1,5 +1,6 @@ using System; using System.ComponentModel.Design; +using System.Diagnostics; using System.Runtime.InteropServices; using System.Threading; using System.Threading.Tasks; @@ -30,6 +31,13 @@ public Pane() { BitmapImageMoniker = KnownMonikers.StatusInformation; ToolBar = new CommandID(PackageGuids.TestExtension, PackageIds.RunnerWindowToolbar); + WindowFrameAvailable += (_, _) => Debug.WriteLine("RunnerWindow frame is now available"); + } + + public override void OnToolWindowCreated() + { + base.OnToolWindowCreated(); + GetWindowFrame().OnShow += (_, args) => Debug.WriteLine($"RunnerWindow state changed: {args.Reason}"); } } }