You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在VSIX项目的WPF UserControl中注入IServiceProvider

问题

开发VSIX项目时,自定义VSIconControl用于根据文件扩展名显示VS内置图标,但控件仅提供带IServiceProvider参数的构造函数,导致无法直接在XAML中声明使用。

解决方案1:添加无参构造函数并全局获取服务

利用VSIX环境中的全局服务提供者,给控件添加无参构造函数,内部自动获取全局服务:

修改VSIconControl的构造函数部分:

namespace Morris.FeatureExplorer.Features.FeatureExplorer;

public partial class VSIconControl : UserControl
{
    private readonly IServiceProvider ServiceProvider;

    // 保留带参数构造函数(用于代码实例化场景)
    public VSIconControl(IServiceProvider serviceProvider)
    {
        ServiceProvider = serviceProvider;
        InitializeComponent();
        UpdateIcon();
    }

    // 添加无参构造函数,供XAML实例化使用
    public VSIconControl() : this(ServiceProvider.GlobalProvider)
    {
    }

    // 其余原有代码保持不变...
}

修改后即可直接在XAML中使用:

<StackPanel Orientation="Horizontal">
  <featureui:VSIconControl FileExtension=".cs"/>
  <TextBlock Text="{Binding Name}" Margin="5"/>
</StackPanel>

解决方案2:将IServiceProvider设为依赖属性(更灵活)

如果需要在不同场景注入不同的服务实例,可将ServiceProvider改为依赖属性,同时提供无参构造函数:

namespace Morris.FeatureExplorer.Features.FeatureExplorer;

public partial class VSIconControl : UserControl
{
    // 将ServiceProvider改为依赖属性,默认值为全局服务提供者
    public static readonly DependencyProperty ServiceProviderProperty =
        DependencyProperty.Register("ServiceProvider", typeof(IServiceProvider), typeof(VSIconControl), 
            new PropertyMetadata(ServiceProvider.GlobalProvider, OnServiceProviderChanged));

    public static readonly DependencyProperty FileExtensionProperty =
         DependencyProperty.Register("FileExtension", typeof(string), typeof(VSIconControl), new PropertyMetadata(null, OnFileExtensionChanged));

    public IServiceProvider ServiceProvider
    {
        get { return (IServiceProvider)GetValue(ServiceProviderProperty); }
        set { SetValue(ServiceProviderProperty, value); }
    }

    public string FileExtension
    {
        get { return (string)GetValue(FileExtensionProperty); }
        set { SetValue(FileExtensionProperty, value); }
    }

    // 无参构造函数
    public VSIconControl()
    {
        InitializeComponent();
        UpdateIcon();
    }

    // 服务提供者变更时重新更新图标
    private static void OnServiceProviderChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        var control = d as VSIconControl;
        control?.UpdateIcon();
    }

    // 其余原有代码保持不变...
}

XAML中可直接使用,也可自定义注入服务:

<StackPanel Orientation="Horizontal">
  <featureui:VSIconControl FileExtension=".cs" ServiceProvider="{Binding CustomServiceProvider}"/>
  <TextBlock Text="{Binding Name}" Margin="5"/>
</StackPanel>

补充优化:线程安全处理

原代码中ThreadHelper.ThrowIfNotOnUIThread()在异步场景下可能引发异常,可改用JoinableTaskFactory安全切换线程:

private ImageMoniker GetImageMonikerForFileExtension(IServiceProvider serviceProvider, string fileExtension)
{
    return ThreadHelper.JoinableTaskFactory.Run(async () =>
    {
        await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync();
        IVsImageService2 imageService = serviceProvider.GetService(typeof(SVsImageService)) as IVsImageService2;
        if (imageService == null)
        {
            throw new InvalidOperationException("Failed to get IVsImageService2.");
        }
        return imageService.GetImageMonikerForFile(fileExtension);
    });
}

private ImageSource GetImageForMoniker(IServiceProvider serviceProvider, ImageMoniker moniker)
{
    return ThreadHelper.JoinableTaskFactory.Run(async () =>
    {
        await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync();
        IVsImageService2 imageService = serviceProvider.GetService(typeof(SVsImageService)) as IVsImageService2;
        if (imageService is null)
            throw new InvalidOperationException("Failed to get IVsImageService2.");
        return imageService.GetImage(moniker, new ImageAttributes()) as ImageSource;
    });
}

内容的提问来源于stack exchange,提问作者Peter Morris

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.10 11:23:14