| title | InfoBars and Notifications |
|---|---|
| description | Learn how to display non-modal informational messages using InfoBars. |
| category | fundamentals |
| order | 11 |
import Callout from '@components/Callout.astro';
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.
// Simple InfoBar in a document
var docView = await VS.Documents.GetActiveDocumentViewAsync();
var infoBar = await VS.InfoBar.CreateAsync(docView, "NuGet packages need to be restored.");
await infoBar.TryShowInfoBarUIAsync();var model = new InfoBarModel(
new[]
{
new InfoBarTextSpan("This file has been modified outside the editor. "),
new InfoBarHyperlink("Reload", "reload"),
new InfoBarTextSpan(" | "),
new InfoBarHyperlink("Ignore", "ignore")
},
KnownMonikers.StatusWarning,
isCloseButtonVisible: true);
var infoBar = await VS.InfoBar.CreateAsync(docView, model);
infoBar.ActionItemClicked += (s, e) =>
{
switch (e.ActionItem.ActionContext)
{
case "reload":
ReloadDocument();
break;
case "ignore":
// Do nothing
break;
}
e.InfoBarUIElement.Close();
};
await infoBar.TryShowInfoBarUIAsync();The InfoBarModel class defines the content and appearance:
var model = new InfoBarModel(
textSpans: new[]
{
new InfoBarTextSpan("Your message here"),
new InfoBarHyperlink("Click Me", "action1")
},
image: KnownMonikers.StatusInformation,
isCloseButtonVisible: true);| Type | Description |
|---|---|
InfoBarTextSpan |
Plain text |
InfoBarHyperlink |
Clickable link with action context |
InfoBarButton |
Button-styled action (VS 2019+) |
new InfoBarTextSpan("Regular text. "),
new InfoBarHyperlink("Link text", actionContext: "myAction"),
new InfoBarButton("Button", actionContext: "buttonAction")Use KnownMonikers for standard icons:
| Icon | Usage |
|---|---|
KnownMonikers.StatusInformation |
General information |
KnownMonikers.StatusWarning |
Warnings |
KnownMonikers.StatusError |
Errors |
KnownMonikers.StatusOK |
Success/confirmation |
KnownMonikers.StatusHelp |
Help/tips |
// Get the active document
var docView = await VS.Documents.GetActiveDocumentViewAsync();
var infoBar = await VS.InfoBar.CreateAsync(docView, "Message for this document");
await infoBar.TryShowInfoBarUIAsync();// In your tool window class
var model = new InfoBarModel("Update available for this extension.");
var infoBar = await VS.InfoBar.CreateAsync(MyToolWindow, model);
await infoBar.TryShowInfoBarUIAsync();var model = new InfoBarModel(
new[] { new InfoBarTextSpan("A new version of this extension is available.") },
KnownMonikers.StatusInformation);
var infoBar = await VS.InfoBar.CreateAsync(model);
await infoBar.TryShowInfoBarUIAsync();var model = new InfoBarModel(
new[]
{
new InfoBarTextSpan("Configuration file is missing. "),
new InfoBarHyperlink("Create", "create"),
new InfoBarHyperlink("Learn More", "help")
},
KnownMonikers.StatusWarning);
var infoBar = await VS.InfoBar.CreateAsync(model);
infoBar.ActionItemClicked += async (sender, args) =>
{
var action = args.ActionItem.ActionContext as string;
switch (action)
{
case "create":
await CreateConfigFileAsync();
args.InfoBarUIElement.Close();
break;
case "help":
System.Diagnostics.Process.Start("https://docs.example.com/config");
// Don't close - user might want to create after reading
break;
}
};
await infoBar.TryShowInfoBarUIAsync();// User clicks close button (automatic)
// Or programmatically:
infoBar.ActionItemClicked += (s, e) =>
{
e.InfoBarUIElement.Close();
};
// Or track the UI element
IVsInfoBarUIElement _infoBarUI;
var infoBar = await VS.InfoBar.CreateAsync(model);
infoBar.ActionItemClicked += (s, e) => _infoBarUI = e.InfoBarUIElement;
await infoBar.TryShowInfoBarUIAsync();
// Later...
_infoBarUI?.Close();Using IVsInfoBarUIFactory directly:
public class InfoBarService
{
private readonly IServiceProvider _serviceProvider;
public InfoBarService(IServiceProvider serviceProvider)
{
_serviceProvider = serviceProvider;
}
public async Task ShowInfoBarAsync(IVsWindowFrame frame, string message)
{
await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync();
var factory = (IVsInfoBarUIFactory)_serviceProvider.GetService(typeof(SVsInfoBarUIFactory));
var host = frame as IVsInfoBarHost;
if (factory == null || host == null) return;
var model = new InfoBarModel(
new[] { new InfoBarTextSpan(message) },
KnownMonikers.StatusInformation,
isCloseButtonVisible: true);
var uiElement = factory.CreateInfoBar(model);
uiElement.Advise(new InfoBarEvents(), out _);
host.AddInfoBar(uiElement);
}
}
public class InfoBarEvents : IVsInfoBarUIEvents
{
public void OnClosed(IVsInfoBarUIElement infoBarUIElement)
{
// InfoBar was closed
}
public void OnActionItemClicked(IVsInfoBarUIElement infoBarUIElement, IVsInfoBarActionItem actionItem)
{
ThreadHelper.ThrowIfNotOnUIThread();
var context = actionItem.ActionContext as string;
// Handle action...
}
}An extension that suggests enabling a feature:
public class FeatureSuggestionService
{
private IVsInfoBarUIElement _currentInfoBar;
private const string SettingKey = "FeatureXEnabled";
public async Task SuggestFeatureAsync()
{
// Don't show if already enabled or dismissed
if (await IsFeatureEnabledAsync() || await WasDismissedAsync())
return;
var model = new InfoBarModel(
new IVsInfoBarTextSpan[]
{
new InfoBarTextSpan("Feature X can improve your productivity. "),
new InfoBarHyperlink("Enable", "enable"),
new InfoBarTextSpan(" | "),
new InfoBarHyperlink("Learn More", "learn"),
new InfoBarTextSpan(" | "),
new InfoBarHyperlink("Don't show again", "dismiss")
},
KnownMonikers.StatusInformation,
isCloseButtonVisible: true);
var infoBar = await VS.InfoBar.CreateAsync(model);
infoBar.ActionItemClicked += async (sender, args) =>
{
var action = args.ActionItem.ActionContext as string;
switch (action)
{
case "enable":
await EnableFeatureAsync();
await VS.StatusBar.ShowMessageAsync("Feature X enabled!");
args.InfoBarUIElement.Close();
break;
case "learn":
await VS.Commands.ExecuteAsync(
"View.WebBrowser",
"https://example.com/feature-x");
break;
case "dismiss":
await SaveDismissedAsync();
args.InfoBarUIElement.Close();
break;
}
};
if (await infoBar.TryShowInfoBarUIAsync())
{
// Track so we can close programmatically if needed
_currentInfoBar = null; // Get from event if needed
}
}
private Task<bool> IsFeatureEnabledAsync() => Task.FromResult(false);
private Task<bool> WasDismissedAsync() => Task.FromResult(false);
private Task EnableFeatureAsync() => Task.CompletedTask;
private Task SaveDismissedAsync() => Task.CompletedTask;
}Show an InfoBar for the entire solution:
public async Task ShowSolutionInfoBarAsync(string message)
{
await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync();
var shell = (IVsShell)await VS.Services.GetShellAsync();
shell.GetProperty((int)__VSSPROPID7.VSSPROPID_MainWindowInfoBarHost, out var hostObj);
if (hostObj is IVsInfoBarHost host)
{
var factory = (IVsInfoBarUIFactory)
await VS.Services.GetInfoBarUIFactoryAsync();
var model = new InfoBarModel(message);
var uiElement = factory.CreateInfoBar(model);
host.AddInfoBar(uiElement);
}
}- Be concise - InfoBars should be scannable at a glance
- Provide actions - Don't just inform; help users act
- Allow dismissal - Always include a close button or "Don't show again"
- Don't spam - One InfoBar at a time per location
- Use appropriate icons - Match the severity/type of message
- Remember preferences - If dismissed, don't show again (persist the choice)
| Scenario | Recommended UI |
|---|---|
| "Feature X is available" | InfoBar |
| "File modified externally" | InfoBar with Reload action |
| "Build failed with errors" | Error List |
| "Operation in progress" | Progress Indication |
| "5 files processed" | Status Bar message |
| "Are you sure you want to delete?" | Modal Dialog |
| "Extension updated, restart required" | InfoBar |
- Output Window - Detailed logging
- Error List - Errors and warnings
- Status Bar - Brief status messages
- Progress Indication - Long-running operations