Skip to content

Commit 2f8d335

Browse files
Merge pull request #437 from reduckted/feature/tool-window-window-frame
Get a WindowFrame wrapper for a tool window
2 parents 912c346 + 63fc5c3 commit 2f8d335

File tree

2 files changed

+78
-0
lines changed

2 files changed

+78
-0
lines changed

demo/VSSDK.TestExtension/ToolWindows/RunnerWindow.cs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using System;
22
using System.ComponentModel.Design;
3+
using System.Diagnostics;
34
using System.Runtime.InteropServices;
45
using System.Threading;
56
using System.Threading.Tasks;
@@ -30,6 +31,13 @@ public Pane()
3031
{
3132
BitmapImageMoniker = KnownMonikers.StatusInformation;
3233
ToolBar = new CommandID(PackageGuids.TestExtension, PackageIds.RunnerWindowToolbar);
34+
WindowFrameAvailable += (_, _) => Debug.WriteLine("RunnerWindow frame is now available");
35+
}
36+
37+
public override void OnToolWindowCreated()
38+
{
39+
base.OnToolWindowCreated();
40+
GetWindowFrame().OnShow += (_, args) => Debug.WriteLine($"RunnerWindow state changed: {args.Reason}");
3341
}
3442
}
3543
}

src/toolkit/Community.VisualStudio.Toolkit.Shared/Windows/ToolkitToolWindowPane.cs

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using System;
22
using Microsoft.VisualStudio.Shell;
3+
using Microsoft.VisualStudio.Shell.Interop;
34

45
namespace Community.VisualStudio.Toolkit
56
{
@@ -10,6 +11,8 @@ namespace Community.VisualStudio.Toolkit
1011
public abstract class ToolkitToolWindowPane : ToolWindowPane
1112
{
1213
private bool _isInitialized;
14+
private WindowFrame? _windowFrame;
15+
private bool _isWindowFrameAvailable;
1316

1417
/// <inheritdoc/>
1518
protected override void Initialize()
@@ -22,5 +25,72 @@ protected override void Initialize()
2225
internal bool IsInitialized => _isInitialized;
2326

2427
internal event EventHandler? Initialized;
28+
29+
/// <inheritdoc/>
30+
public override void OnToolWindowCreated()
31+
{
32+
base.OnToolWindowCreated();
33+
_isWindowFrameAvailable = true;
34+
WindowFrameAvailable?.Invoke(this, EventArgs.Empty);
35+
}
36+
37+
/// <summary>
38+
/// Indicates whether the <see cref="GetWindowFrame"/> method can be called.
39+
/// </summary>
40+
public bool IsWindowFrameAvailable => _isWindowFrameAvailable;
41+
42+
/// <summary>
43+
/// Raised when Visual Studio creates the tool window's frame.
44+
/// The <see cref="WindowFrame"/> property can be accessed from this point onwards.
45+
/// </summary>
46+
public event EventHandler? WindowFrameAvailable;
47+
48+
/// <summary>
49+
/// Gets the tool window's window frame.
50+
/// <para>
51+
/// This method can only be called after Visual Studio has created the window frame.
52+
/// You can detect this in various ways:
53+
/// <list type="bullet">
54+
/// <item>
55+
/// Override the <see cref="OnToolWindowCreated"/> method.
56+
/// When this method is called, the window frame will be available.
57+
/// </item>
58+
/// <item>
59+
/// Listen for the <see cref="WindowFrameAvailable"/> event.
60+
/// When the event is raised, the window frame will be available.
61+
/// </item>
62+
/// <item>
63+
/// Check the <see cref="IsWindowFrameAvailable"/> property.
64+
/// </item>
65+
/// </list>
66+
/// </para>
67+
/// </summary>
68+
/// <exception cref="InvalidOperationException">The window frame is not available.</exception>
69+
protected WindowFrame GetWindowFrame()
70+
{
71+
if (_windowFrame is null)
72+
{
73+
// The `Frame` property has to be set by Visual Studio, so it might
74+
// be null at this point. It's also typed as an `object` even though
75+
// internally it's stored as an `IVsWindowFrame`, so we can use
76+
// type matching to both cast and confirm that it's not null.
77+
if (Frame is IVsWindowFrame vsWindowFrame)
78+
{
79+
// We could create the WindowFrame in `OnToolWindowCreated`,
80+
// but we delay-create it for two reasons:
81+
// 1. It may not ever be needed.
82+
// 2. If a derived class also overrides `OnToolWindowCreated`, then the window
83+
// frame would only be available after it called `base.OnToolWindowCreated()`.
84+
// Delay-creating it means that it will be available before that call is made.
85+
_windowFrame = new WindowFrame(vsWindowFrame);
86+
}
87+
else
88+
{
89+
throw new InvalidOperationException("The tool window's frame is not available yet.");
90+
}
91+
}
92+
93+
return _windowFrame;
94+
}
2595
}
2696
}

0 commit comments

Comments
 (0)