如何在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
相关产品推荐
相关产品推荐

