Тип не включает никаких доступных конструкторов WPF - нет оконного приложения - PullRequest
0 голосов
/ 09 июля 2019

Я использую этот пакет для создания и использования значка уведомления, что означает, что в моем приложении нет Windows, только ResourceDictionary и ViewModel

Все работает нормально, пока я не изменил свой конструктор, чтобы он принимал интерфейс с использованием DI-фреймворка (я использую расширение Autofac для PRISM [Prism.Autofac]).

Если я добавляю конструктор без параметров, все работает нормально

Должен ли я даже использовать Autofac, это слишком? как я могу сделать DI?

Примечания

  • я использую PRISM в качестве основы MVVM
  • Интерфейс находится в другом проекте
  • Я уже прочитал это , это , это и ObjectDataProvider документации и не смогли найти решение

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 Ответ

1 голос
/ 09 июля 2019

Это не работает, потому что вы устанавливаете экземпляр 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 или хостинг Window TaskbarIcon для использования композиции:

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;      
    }
}

В MainWindow.xaml свяжите свойство с ContentPresenter:

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

Затем настройте контейнер Autofac:

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();
    }
}

Затем загрузите приложение:

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();         
    }
}

Таким образом, вы избавились от ObjectDataProvider, так как вместо этого вы используете Autofac.

...