Reputation: 1915
I'm using this package to create and use notify icon, which means I have no Windows in my app just ResourceDictionary
and ViewModel
It all is working fine and well until I changed my constructor to accept an interface using DI framework(I'm using Autofac extension to PRISM [Prism.Autofac]).
If I adding back the parameterless constructor everything works just fine
Should I even use Autofac is is overkill? how can I do the DI?
Notes
ObjectDataProvider
documentation and could not find any solutionApp.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 (Constructor only)
public NotifyIconViewModel(IServiceControllerWorker ServiceControllerWorker)
{
_serviceControllerWorker = ServiceControllerWorker;
}
Upvotes: 0
Views: 1192
Reputation: 28968
It is not working because you are setting the ObjectDataProvider
instance to the DataContext
<tb:TaskbarIcon.DataContext>
<ObjectDataProvider ObjectType="{x:Type local:NotifyIconViewModel}">
<ObjectDataProvider.ConstructorParameters>
<interface:ServiceControllerWorker />
</ObjectDataProvider.ConstructorParameters>
</ObjectDataProvider>
</tb:TaskbarIcon.DataContext>
instead of the value of the ObjectDataProvider
.
Declare the provider in a ResourceDictionary
:
<ResourceDictionary>
<ObjectDataProvider x:Key="ViewModelProvider" ObjectType="{x:Type local:NotifyIconViewModel}">
<ObjectDataProvider.ConstructorParameters>
<interface:ServiceControllerWorker />
</ObjectDataProvider.ConstructorParameters>
</ObjectDataProvider>
</ResourceDictionary>
And bind it to the DataContext
:
<tb:TaskbarIcon DataContext="{Binding Source={StaticResource ViewModelProvider}}" />
The binding will make the provider to instantiate the provided instance.
But since you are creating the instance with the help of ObjectDataProvider
you made the Autofac container or dependency injection redundant. If you want to use dependency injection, you must let Autofac create the instances. This requires the application to be started manually and to rewrite the MainWindow
or the hosting Window
of the TaskbarIcon
to use composition:
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;
}
}
In the MainWindow.xaml bind the property to a ContentPresenter
:
<Window>
<ContentPresenter Content="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=MainWindow}, Path=NotifyIcon} />
</Window>
Then configure the Autofac container:
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();
}
}
Then bootstrap the application:
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();
}
}
This way you got rid of the ObjectDataProvider
, since you are using Autofac instead.
Upvotes: 1