Merq.VisualStudio
3.0.0-beta
Prefix Reserved
dotnet add package Merq.VisualStudio --version 3.0.0-beta
NuGet\Install-Package Merq.VisualStudio -Version 3.0.0-beta
<PackageReference Include="Merq.VisualStudio" Version="3.0.0-beta" />
<PackageVersion Include="Merq.VisualStudio" Version="3.0.0-beta" />
<PackageReference Include="Merq.VisualStudio" />
paket add Merq.VisualStudio --version 3.0.0-beta
#r "nuget: Merq.VisualStudio, 3.0.0-beta"
#:package Merq.VisualStudio@3.0.0-beta
#addin nuget:?package=Merq.VisualStudio&version=3.0.0-beta&prerelease
#tool nuget:?package=Merq.VisualStudio&version=3.0.0-beta&prerelease
Open Source Maintenance Fee
To ensure the long-term sustainability of this project, users of this package who generate revenue must pay an Open Source Maintenance Fee. While the source code is freely available under the terms of the License, this package and other aspects of the project require adherence to the Maintenance Fee.
To pay the Maintenance Fee, become a Sponsor at the proper OSMF tier. A single fee covers all of Devlooped packages.
This package provides a MEF-ready customization of the Merq
default implementation, which makes it trivial to consume from
an application that uses Microsoft.VisualStudio.Composition
to load MEF-based components.
This is built-in Visual Studio, and you can take a dependency
on Merq from your project by simply declaring it as a prerequisite
for your extension via the manifest:
<PackageManifest Version="2.0.0" ...>
<Prerequisites>
<Prerequisite Id="Microsoft.VisualStudio.Component.Merq" Version="[17.0,)" DisplayName="Common Xamarin internal tools" />
</Prerequisites>
</PackageManifest>
With that in place, you can get access to the IMessageBus by simply
importing it in your MEF components:
[Export]
[PartCreationPolicy(CreationPolicy.Shared)]
class MyComponent
{
readonly IMessageBus bus;
[ImportingConstructor]
public MyComponent(IMessageBus bus)
{
this.bus = bus;
bus.Observe<OnDidOpenSolution>().Subscribe(OnSolutionOpened);
}
void OnSolutionOpened(OnDidOpenSolution e)
{
// do something, perhaps execute some command?
bus.Execute(new MyCommand("Hello World"));
}
}
If the event handler needs to be async for some reason (i.e. you're pushing additional events or executing async commands), you can use the following pattern:
[ImportingConstructor]
public MyComponent(IMessageBus bus)
{
this.bus = bus;
bus.Observe<ConcreteEvent>()
.Select(value => Observable.FromAsync(async () => await OnSolutionOpened(value)))
.Subscribe();
}
async Task OnSolutionOpened(ConcreteEvent e)
{
// do something, perhaps execute some command?
await bus.ExecuteAsync(new MyCommand("Hello World"));
// perhaps raise further events?
await bus.NotifyAsync(new MyEvent(42));
}
To export command handlers to VS, you must export them with the relevant interface they implement, such as:
public record OpenSolution(string Path) : IAsyncCommand;
[Export(typeof(IAsyncCommandHandler<OpenSolution>))]
public class OpenSolutionHandler : IAsyncCommandHandler<OpenSolution>
{
public bool CanExecute(OpenSolution command)
=> !string.IsNullOrEmpty(command.Path) && File.Exists(command.Path);
public Task ExecuteAsync(OpenSolution command, CancellationToken cancellation)
{
// switch to main thread
// invoke relevant VS API
}
}
Events can be notified directly on the bus, as shown in the first example,
or can be produced externally. For example, the producer of OnDidOpenSolution
would look like the following:
public record OnDidOpenSolution(string Path);
[Export(typeof(IObservable<OnDidOpenSolution>))]
public class OnDidOpenSolutionObservable : IObservable<OnDidOpenSolution>, IVsSolutionEvents
{
readonly JoinableTaskContext jtc;
readonly Subject<OnDidOpenSolution> subject = new();
readonly AsyncLazy<IVsSolution?> lazySolution;
[ImportingConstructor]
public OnDidOpenSolutionObservable(
[Import(typeof(SVsServiceProvider))] IServiceProvider servideProvider,
JoinableTaskContext joinableTaskContext)
{
jtc = joinableTaskContext;
lazySolution = new AsyncLazy<IVsSolution?>(async () =>
{
await jtc.Factory.SwitchToMainThreadAsync();
var solution = servideProvider.GetService<SVsSolution, IVsSolution>();
solution.AdviseSolutionEvents(this, out var _);
return solution;
}, jtc.Factory);
}
public IDisposable Subscribe(IObserver<OnDidOpenSolution> observer)
=> subject.Subscribe(observer);
int IVsSolutionEvents.OnAfterOpenSolution(object pUnkReserved, int fNewSolution)
{
var path = jtc.Factory.Run(async () =>
{
if (await lazySolution.GetValueAsync() is IVsSolution solution &&
ErrorHandler.Succeeded(solution.GetSolutionInfo(out var _, out var slnFile, out var _)) &&
!string.IsNullOrEmpty(slnFile))
{
return slnFile;
}
return null;
});
if (path is string)
{
subject.OnNext(new OnDidOpenSolution(path));
}
return VSConstants.S_OK;
}
// rest of IVsSolutionEvents members
}
The implementation above is just an example, but wouldn't be too far from a real one using the IVs* APIs. It's worth remembering how simple this is to consume though:
[ImportingConstructor]
public MyComponent(IMessageBus bus)
{
bus.Observe<OnDidOpenSolution>().Subscribe(OnSolutionOpened);
}
void OnSolutionOpened(OnDidOpenSolution e)
{
// ...
}
The benefit of the external producer implementing IObservable<T> itself is that
it won't be instantiated at all unless someone called Observe<T>, which minimizes
the startup and ongoing cost of having this extensibility mechanism built-in.
If you are hosting VS MEF in your app, the same concepts apply, so it should be a familiar experience.
Sponsors
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET Framework | net472 is compatible. net48 was computed. net481 was computed. |
-
.NETFramework 4.7.2
- Merq (>= 3.0.0-beta)
- Merq.Abstractions (>= 3.0.0-beta)
- Microsoft.NETFramework.ReferenceAssemblies (>= 1.0.3)
- Microsoft.VisualStudio.ComponentModelHost (>= 17.3.198)
- Microsoft.VisualStudio.Shell.Framework (>= 17.3.32804.24)
- System.ComponentModel.Composition (>= 6.0.0)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 3.0.0-beta | 205 | 11/12/2025 |
| 3.0.0-alpha | 174 | 6/17/2025 |
| 2.0.0 | 302 | 1/29/2024 |
| 2.0.0-rc.6 | 83 | 1/29/2024 |
| 2.0.0-rc.5 | 86 | 1/27/2024 |
| 2.0.0-rc.3 | 164 | 7/10/2023 |
| 2.0.0-rc.2 | 179 | 7/10/2023 |
| 2.0.0-rc.1 | 173 | 7/7/2023 |
| 2.0.0-beta.4 | 181 | 7/6/2023 |
| 2.0.0-beta.3 | 212 | 11/19/2022 |
| 2.0.0-beta.2 | 194 | 11/18/2022 |
| 2.0.0-beta | 278 | 11/16/2022 |
| 2.0.0-alpha | 259 | 11/16/2022 |
| 1.5.0 | 626 | 2/16/2023 |
| 1.3.0 | 674 | 7/28/2022 |
| 1.2.0-beta | 271 | 7/20/2022 |
| 1.2.0-alpha | 287 | 7/16/2022 |