该类型不包括任何可访问的构造函数WPF-无窗口应用程序

时间:2019-07-09 08:38:20

标签: c# wpf mvvm dependency-injection prism

我正在使用this包来创建和使用通知图标,这意味着我的应用程序中仅ResourceDictionaryViewModel中没有Windows

一切正常,直到我更改构造函数以使用DI框架接受接口为止(我正在使用 Autofac 对PRISM [Prism.Autofac]的扩展)。

如果我重新添加无参数构造函数,那么一切都会很好

我应该使用Autofac还是太过分了?我怎么做DI?

注释

  • 使用 PRISM 作为我的MVVM框架
  • 该界面位于另一个项目中
  • 我已经阅读了thisthisthisObjectDataProvider文档,找不到任何解决方案

App.xaml.cs

    public partial class App : Application
    {
        private TaskbarIcon notifyIcon;

        protected override void OnStartup(StartupEventArgs e)
        {
            base.OnStartup(e);

            var bootstrapper = new Bootstrapper();
            bootstrapper.Run();

            notifyIcon = (TaskbarIcon)FindResource("NotifyIcon");          
        }

        protected override void OnExit(ExitEventArgs e)
        {
            notifyIcon.Dispose();
            base.OnExit(e);
        }
    }

App.xaml

  <Application.Resources>
    <ResourceDictionary>
      <ResourceDictionary.MergedDictionaries>
        <ResourceDictionary Source="NotifyIconResources.xaml" />
      </ResourceDictionary.MergedDictionaries>
    </ResourceDictionary>
  </Application.Resources>

NotifyIconResources.xaml

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                    xmlns:tb="http://www.hardcodet.net/taskbar"
                    xmlns:local ="clr-namespace:WatchDog"
                    xmlns:interface="clr-namespace:ServiceControllerLibary;assembly=ServiceControllerLibary"
                    >
    <local:ServiceControllerWorkerStatusToIconConverter x:Key="ServiceControllerWorkerStatusToIconConverter"/>

    <ContextMenu x:Shared="false" x:Key="SysTrayMenu">
        <MenuItem Header="Show Window" />
        <MenuItem Header="Hide Window" />
        <Separator />
        <MenuItem Header="Exit" />
    </ContextMenu>  


    <tb:TaskbarIcon x:Key="NotifyIcon"
                    ToolTipText ="{Binding ToolTipText}" DoubleClickCommand="{Binding}"
                    ContextMenu="{StaticResource SysTrayMenu}"
                    IconSource="{Binding ToolTipStatus, 
                    Converter={StaticResource ServiceControllerWorkerStatusToIconConverter}
                    , Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" >

        <!-- Original Not Working-->
        <!-- self-assign a data context (could also be done programmatically) -->
        <!--<tb:TaskbarIcon.DataContext>
            <local:NotifyIconViewModel/>
        </tb:TaskbarIcon.DataContext>-->

        <!-- 2nd try Not Working-->
        <tb:TaskbarIcon.DataContext>
            <ObjectDataProvider ObjectType="{x:Type local:NotifyIconViewModel}">
                <ObjectDataProvider.ConstructorParameters>
                    <interface:ServiceControllerWorker />
                </ObjectDataProvider.ConstructorParameters>
            </ObjectDataProvider>
        </tb:TaskbarIcon.DataContext>

    </tb:TaskbarIcon>


</ResourceDictionary>

Bootstrapper.cs

    class Bootstrapper : AutofacBootstrapper 
    {
        protected override void ConfigureContainerBuilder(ContainerBuilder builder)
        {
            base.ConfigureContainerBuilder(builder);

            builder.RegisterType<ServiceControllerWorker>().As<IServiceControllerWorker>().SingleInstance();

        }
    }

NotifyIconViewModel.cs(仅限构造函数)

 public NotifyIconViewModel(IServiceControllerWorker ServiceControllerWorker)
 {    
      _serviceControllerWorker = ServiceControllerWorker;
  }

1 个答案:

答案 0 :(得分:1)

它不起作用,因为您将ObjectDataProvider实例设置为DataContext

<tb:TaskbarIcon.DataContext>
    <ObjectDataProvider ObjectType="{x:Type local:NotifyIconViewModel}">
        <ObjectDataProvider.ConstructorParameters>
            <interface:ServiceControllerWorker />
        </ObjectDataProvider.ConstructorParameters>
    </ObjectDataProvider>
</tb:TaskbarIcon.DataContext>

代替ObjectDataProvider
ResourceDictionary中声明提供者:

<ResourceDictionary>    
    <ObjectDataProvider x:Key="ViewModelProvider" ObjectType="{x:Type local:NotifyIconViewModel}">
        <ObjectDataProvider.ConstructorParameters>
            <interface:ServiceControllerWorker />
        </ObjectDataProvider.ConstructorParameters>
    </ObjectDataProvider>
</ResourceDictionary>

并将其绑定到DataContext

<tb:TaskbarIcon DataContext="{Binding Source={StaticResource ViewModelProvider}}" />

绑定将使提供程序实例化所提供的实例。

但是,由于要在ObjectDataProvider的帮助下创建实例,因此使Autofac容器或依赖项注入变得多余。如果要使用依赖项注入,则必须让Autofac创建实例。这要求应用程序手动启动,并重写MainWindow的{​​{1}}或托管Window才能使用组合:

TaskbarIcon

在MainWindow.xaml中,将该属性绑定到public partial class MainWindow : Window { public static readonly DependencyProperty NotifyIconProperty = DependencyProperty.Register( "NotifyIcon", typeof(TaskbarIcon), typeof(Window), new PropertyMetadata(default(TaskbarIcon))); public TaskbarIcon NotifyIcon { get { return (TaskbarIcon) GetValue(MainWindow.NotifyIconProperty); } set { SetValue(MainWindow.NotifyIconProperty, value); } } public MainWindow(TaskbarIcon taskbarIcon, INotifyIconViewModel notifyIconDataContext, IViewModel dataContext) { this.notifyIcon = taskbarIcon; this.notifyIcon.DataContext = notifyIconDataContext; this.DataContext = dataContext; } }

ContentPresenter

然后配置Autofac容器:

<Window>
    <ContentPresenter Content="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=MainWindow}, Path=NotifyIcon} />
</Window>

然后引导应用程序:

class Bootstrapper : AutofacBootstrapper 
{
    public Container ConfigureContainerBuilder()
    {
        var builder = new ContainerBuilder();

        builder.RegisterType<ServiceControllerWorker>().As<IServiceControllerWorker>().SingleInstance();
        builder.RegisterType<NotifyIconViewModel>().As<INotifyIconViewModel>().SingleInstance();
        builder.RegisterType<TaskbarIcon>().SingleInstance();
        builder.RegisterType<MainWindow>().SingleInstance();

        return builder.Build();
    }
}

由于您使用的是Autofac,因此您摆脱了public partial class App : Application { protected override void OnStartup(StartupEventArgs e) { base.OnStartup(e); var bootstrapper = new Bootstrapper(); var container = bootstrapper.ConfigureContainerBuilder(); Application.Current.MainWindow = container.Resolve<MainWindow>(); Application.Current.MainWindow.Show(); } }