Skip to content

Commit e023bbf

Browse files
committed
docs(ui): add InfoBars and Notifications documentation
Add comprehensive documentation for InfoBars: - Quick start with Community Toolkit - InfoBar with actions (hyperlinks and buttons) - InfoBarModel configuration - Text spans and icons reference - Showing InfoBars in documents, tool windows, and main window - Handling action clicks - Closing InfoBars programmatically - Traditional IVsInfoBarUIFactory approach - Complete feature suggestion example - Best practices and when to use InfoBars vs other UI Closes #41
1 parent f8a0433 commit e023bbf

1 file changed

Lines changed: 361 additions & 0 deletions

File tree

src/content/docs/infobars.mdx

Lines changed: 361 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,361 @@
1+
---
2+
title: InfoBars and Notifications
3+
description: Learn how to display non-modal informational messages using InfoBars.
4+
category: fundamentals
5+
order: 11
6+
---
7+
8+
import Callout from '@components/Callout.astro';
9+
10+
InfoBars are non-modal notification banners that appear at the top of documents, tool windows, or the main VS window. They're ideal for suggestions, warnings, or actions that don't require immediate attention.
11+
12+
## Quick Start with Community Toolkit
13+
14+
```csharp
15+
// Simple InfoBar in a document
16+
var docView = await VS.Documents.GetActiveDocumentViewAsync();
17+
var infoBar = await VS.InfoBar.CreateAsync(docView, "NuGet packages need to be restored.");
18+
await infoBar.TryShowInfoBarUIAsync();
19+
```
20+
21+
## InfoBar with Actions
22+
23+
```csharp
24+
var model = new InfoBarModel(
25+
new[]
26+
{
27+
new InfoBarTextSpan("This file has been modified outside the editor. "),
28+
new InfoBarHyperlink("Reload", "reload"),
29+
new InfoBarTextSpan(" | "),
30+
new InfoBarHyperlink("Ignore", "ignore")
31+
},
32+
KnownMonikers.StatusWarning,
33+
isCloseButtonVisible: true);
34+
35+
var infoBar = await VS.InfoBar.CreateAsync(docView, model);
36+
infoBar.ActionItemClicked += (s, e) =>
37+
{
38+
switch (e.ActionItem.ActionContext)
39+
{
40+
case "reload":
41+
ReloadDocument();
42+
break;
43+
case "ignore":
44+
// Do nothing
45+
break;
46+
}
47+
e.InfoBarUIElement.Close();
48+
};
49+
50+
await infoBar.TryShowInfoBarUIAsync();
51+
```
52+
53+
## InfoBarModel
54+
55+
The `InfoBarModel` class defines the content and appearance:
56+
57+
```csharp
58+
var model = new InfoBarModel(
59+
textSpans: new[]
60+
{
61+
new InfoBarTextSpan("Your message here"),
62+
new InfoBarHyperlink("Click Me", "action1")
63+
},
64+
image: KnownMonikers.StatusInformation,
65+
isCloseButtonVisible: true);
66+
```
67+
68+
### Text Spans
69+
70+
| Type | Description |
71+
|------|-------------|
72+
| `InfoBarTextSpan` | Plain text |
73+
| `InfoBarHyperlink` | Clickable link with action context |
74+
| `InfoBarButton` | Button-styled action (VS 2019+) |
75+
76+
```csharp
77+
new InfoBarTextSpan("Regular text. "),
78+
new InfoBarHyperlink("Link text", actionContext: "myAction"),
79+
new InfoBarButton("Button", actionContext: "buttonAction")
80+
```
81+
82+
### Icons
83+
84+
Use `KnownMonikers` for standard icons:
85+
86+
| Icon | Usage |
87+
|------|-------|
88+
| `KnownMonikers.StatusInformation` | General information |
89+
| `KnownMonikers.StatusWarning` | Warnings |
90+
| `KnownMonikers.StatusError` | Errors |
91+
| `KnownMonikers.StatusOK` | Success/confirmation |
92+
| `KnownMonikers.StatusHelp` | Help/tips |
93+
94+
## Showing InfoBars in Different Locations
95+
96+
### In a Document
97+
98+
```csharp
99+
// Get the active document
100+
var docView = await VS.Documents.GetActiveDocumentViewAsync();
101+
var infoBar = await VS.InfoBar.CreateAsync(docView, "Message for this document");
102+
await infoBar.TryShowInfoBarUIAsync();
103+
```
104+
105+
### In a Tool Window
106+
107+
```csharp
108+
// In your tool window class
109+
var model = new InfoBarModel("Update available for this extension.");
110+
var infoBar = await VS.InfoBar.CreateAsync(MyToolWindow, model);
111+
await infoBar.TryShowInfoBarUIAsync();
112+
```
113+
114+
### In the Main Window (Global)
115+
116+
```csharp
117+
var model = new InfoBarModel(
118+
new[] { new InfoBarTextSpan("A new version of this extension is available.") },
119+
KnownMonikers.StatusInformation);
120+
121+
var infoBar = await VS.InfoBar.CreateAsync(model);
122+
await infoBar.TryShowInfoBarUIAsync();
123+
```
124+
125+
## Handling Actions
126+
127+
```csharp
128+
var model = new InfoBarModel(
129+
new[]
130+
{
131+
new InfoBarTextSpan("Configuration file is missing. "),
132+
new InfoBarHyperlink("Create", "create"),
133+
new InfoBarHyperlink("Learn More", "help")
134+
},
135+
KnownMonikers.StatusWarning);
136+
137+
var infoBar = await VS.InfoBar.CreateAsync(model);
138+
139+
infoBar.ActionItemClicked += async (sender, args) =>
140+
{
141+
var action = args.ActionItem.ActionContext as string;
142+
143+
switch (action)
144+
{
145+
case "create":
146+
await CreateConfigFileAsync();
147+
args.InfoBarUIElement.Close();
148+
break;
149+
150+
case "help":
151+
System.Diagnostics.Process.Start("https://docs.example.com/config");
152+
// Don't close - user might want to create after reading
153+
break;
154+
}
155+
};
156+
157+
await infoBar.TryShowInfoBarUIAsync();
158+
```
159+
160+
## Closing an InfoBar
161+
162+
```csharp
163+
// User clicks close button (automatic)
164+
// Or programmatically:
165+
infoBar.ActionItemClicked += (s, e) =>
166+
{
167+
e.InfoBarUIElement.Close();
168+
};
169+
170+
// Or track the UI element
171+
IVsInfoBarUIElement _infoBarUI;
172+
173+
var infoBar = await VS.InfoBar.CreateAsync(model);
174+
infoBar.ActionItemClicked += (s, e) => _infoBarUI = e.InfoBarUIElement;
175+
await infoBar.TryShowInfoBarUIAsync();
176+
177+
// Later...
178+
_infoBarUI?.Close();
179+
```
180+
181+
## Traditional Approach
182+
183+
Using `IVsInfoBarUIFactory` directly:
184+
185+
```csharp
186+
public class InfoBarService
187+
{
188+
private readonly IServiceProvider _serviceProvider;
189+
190+
public InfoBarService(IServiceProvider serviceProvider)
191+
{
192+
_serviceProvider = serviceProvider;
193+
}
194+
195+
public async Task ShowInfoBarAsync(IVsWindowFrame frame, string message)
196+
{
197+
await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync();
198+
199+
var factory = (IVsInfoBarUIFactory)_serviceProvider.GetService(typeof(SVsInfoBarUIFactory));
200+
var host = frame as IVsInfoBarHost;
201+
202+
if (factory == null || host == null) return;
203+
204+
var model = new InfoBarModel(
205+
new[] { new InfoBarTextSpan(message) },
206+
KnownMonikers.StatusInformation,
207+
isCloseButtonVisible: true);
208+
209+
var uiElement = factory.CreateInfoBar(model);
210+
uiElement.Advise(new InfoBarEvents(), out _);
211+
host.AddInfoBar(uiElement);
212+
}
213+
}
214+
215+
public class InfoBarEvents : IVsInfoBarUIEvents
216+
{
217+
public void OnClosed(IVsInfoBarUIElement infoBarUIElement)
218+
{
219+
// InfoBar was closed
220+
}
221+
222+
public void OnActionItemClicked(IVsInfoBarUIElement infoBarUIElement, IVsInfoBarActionItem actionItem)
223+
{
224+
ThreadHelper.ThrowIfNotOnUIThread();
225+
226+
var context = actionItem.ActionContext as string;
227+
// Handle action...
228+
}
229+
}
230+
```
231+
232+
## Complete Example
233+
234+
An extension that suggests enabling a feature:
235+
236+
```csharp
237+
public class FeatureSuggestionService
238+
{
239+
private IVsInfoBarUIElement _currentInfoBar;
240+
private const string SettingKey = "FeatureXEnabled";
241+
242+
public async Task SuggestFeatureAsync()
243+
{
244+
// Don't show if already enabled or dismissed
245+
if (await IsFeatureEnabledAsync() || await WasDismissedAsync())
246+
return;
247+
248+
var model = new InfoBarModel(
249+
new IVsInfoBarTextSpan[]
250+
{
251+
new InfoBarTextSpan("Feature X can improve your productivity. "),
252+
new InfoBarHyperlink("Enable", "enable"),
253+
new InfoBarTextSpan(" | "),
254+
new InfoBarHyperlink("Learn More", "learn"),
255+
new InfoBarTextSpan(" | "),
256+
new InfoBarHyperlink("Don't show again", "dismiss")
257+
},
258+
KnownMonikers.StatusInformation,
259+
isCloseButtonVisible: true);
260+
261+
var infoBar = await VS.InfoBar.CreateAsync(model);
262+
263+
infoBar.ActionItemClicked += async (sender, args) =>
264+
{
265+
var action = args.ActionItem.ActionContext as string;
266+
267+
switch (action)
268+
{
269+
case "enable":
270+
await EnableFeatureAsync();
271+
await VS.StatusBar.ShowMessageAsync("Feature X enabled!");
272+
args.InfoBarUIElement.Close();
273+
break;
274+
275+
case "learn":
276+
await VS.Commands.ExecuteAsync(
277+
"View.WebBrowser",
278+
"https://example.com/feature-x");
279+
break;
280+
281+
case "dismiss":
282+
await SaveDismissedAsync();
283+
args.InfoBarUIElement.Close();
284+
break;
285+
}
286+
};
287+
288+
if (await infoBar.TryShowInfoBarUIAsync())
289+
{
290+
// Track so we can close programmatically if needed
291+
_currentInfoBar = null; // Get from event if needed
292+
}
293+
}
294+
295+
private Task<bool> IsFeatureEnabledAsync() => Task.FromResult(false);
296+
private Task<bool> WasDismissedAsync() => Task.FromResult(false);
297+
private Task EnableFeatureAsync() => Task.CompletedTask;
298+
private Task SaveDismissedAsync() => Task.CompletedTask;
299+
}
300+
```
301+
302+
## InfoBar in Solution Explorer
303+
304+
Show an InfoBar for the entire solution:
305+
306+
```csharp
307+
public async Task ShowSolutionInfoBarAsync(string message)
308+
{
309+
await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync();
310+
311+
var shell = (IVsShell)await VS.Services.GetShellAsync();
312+
shell.GetProperty((int)__VSSPROPID7.VSSPROPID_MainWindowInfoBarHost, out var hostObj);
313+
314+
if (hostObj is IVsInfoBarHost host)
315+
{
316+
var factory = (IVsInfoBarUIFactory)
317+
await VS.Services.GetInfoBarUIFactoryAsync();
318+
319+
var model = new InfoBarModel(message);
320+
var uiElement = factory.CreateInfoBar(model);
321+
322+
host.AddInfoBar(uiElement);
323+
}
324+
}
325+
```
326+
327+
## Best Practices
328+
329+
1. **Be concise** - InfoBars should be scannable at a glance
330+
2. **Provide actions** - Don't just inform; help users act
331+
3. **Allow dismissal** - Always include a close button or "Don't show again"
332+
4. **Don't spam** - One InfoBar at a time per location
333+
5. **Use appropriate icons** - Match the severity/type of message
334+
6. **Remember preferences** - If dismissed, don't show again (persist the choice)
335+
336+
<Callout type="tip">
337+
InfoBars are perfect for non-urgent suggestions like "Would you like to enable..." or "A newer version is available." For errors requiring action, use the Error List instead.
338+
</Callout>
339+
340+
<Callout type="warning">
341+
Don't show InfoBars immediately when VS starts. Wait until the user interacts with relevant features, or delay by several seconds.
342+
</Callout>
343+
344+
## When to Use InfoBars vs Other UI
345+
346+
| Scenario | Recommended UI |
347+
|----------|----------------|
348+
| "Feature X is available" | InfoBar |
349+
| "File modified externally" | InfoBar with Reload action |
350+
| "Build failed with errors" | Error List |
351+
| "Operation in progress" | Progress Indication |
352+
| "5 files processed" | Status Bar message |
353+
| "Are you sure you want to delete?" | Modal Dialog |
354+
| "Extension updated, restart required" | InfoBar |
355+
356+
## See Also
357+
358+
- [Output Window](output-window) - Detailed logging
359+
- [Error List](error-list) - Errors and warnings
360+
- [Status Bar](status-bar) - Brief status messages
361+
- [Progress Indication](progress-indication) - Long-running operations

0 commit comments

Comments
 (0)