I have a WPF application with a NotifyIcon defined in a ResourceDictionary that is added to Application.Current.Resources on App start.
I am using the MVVM-Light framework and I want to bind the Command properties of a ContextMenu.MenuItems on the NotifyIcon to a public RelayCommand defined in a ViewModel.
I am comfortable with coupling a View to a ViewModel but how to I couple a Global Resource to a ViewModel?
Here is my attempt at getting this to work, just not sure if I'm on the correct lines or not...
When I run this code I get an error stating "Cannot find resource named 'Locator'. Resource names are case sensitive." This originates from the DataContext binding on the TaskBarIcon tag in NotificationIconResources.xaml
SingleInstanceManager ensures just one instance can be created
public sealed class SingleInstanceManager : WindowsFormsApplicationBase
{
[STAThread]
public static void Main(string[] args)
{
(new SingleInstanceManager()).Run(args);
}
public SingleInstanceManager()
{
IsSingleInstance = true;
}
public ControllerApp App { get; private set; }
protected override bool OnStartup(StartupEventArgs e)
{
App = new ControllerApp();
App.Run();
return false;
}
protected override void OnStartupNextInstance(StartupNextInstanceEventArgs eventArgs)
{
base.OnStartupNextInstance(eventArgs);
App.MainWindow.Activate();
App.ProcessArgs(eventArgs.CommandLine.ToArray(), false);
}
}
ControllerApp replaces App.xaml and App.xaml.cs
public class ControllerApp : Application
{
public MainWindow window { get; private set; }
bool startMinimized = false;
private TaskbarIcon tb;
public ControllerApp()
: base()
{ }
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
DispatcherHelper.Initialize();
ResourceDictionary dict = new ResourceDictionary();
dict.Source = new Uri("NotificationIconResources.xaml", UriKind.Relative);
Application.Current.Resources.MergedDictionaries.Add(dict);
ViewModel.ViewModelLocator vmLocator = new ViewModel.ViewModelLocator();
Application.Current.Resources.Add("Locator", vmLocator);
window = new MainWindow();
ProcessArgs(e.Args, true);
//initialize NotifyIcon
tb = (TaskbarIcon)FindResource("ItemNotifyIcon");
if (startMinimized)
{
window.WindowState = WindowState.Minimized;
}
window.Show();
}
protected override void OnExit(ExitEventArgs e)
{
base.OnExit(e);
tb.Dispose();
}
public void ProcessArgs(string[] args, bool firstInstance)
{
}
}
NotificationIconResources.xaml is the resource dictionary defining the NotifyIcon
<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">
<tb:TaskbarIcon x:Key="ItemNotifyIcon"
IconSource="/Controller;component/Images/ItemRunning.ico"
IsNoWaitForDoubleClick="True"
ToolTipText="Item is running"
DataContext="{Binding NotifyIcon, Source={StaticResource Locator}}">
<tb:TaskbarIcon.ContextMenu>
<ContextMenu>
<MenuItem Header="Open Control Panel" />
<Separator />
<MenuItem Header="Start Item" Command="{Binding Path=StartServiceCommand}" />
<MenuItem Header="Pause Item" />
<MenuItem Header="Stop Item" Command="{Binding Path=StopServiceCommand}" />
<Separator />
<MenuItem Header="Close" />
</ContextMenu>
</tb:TaskbarIcon.ContextMenu>
</tb:TaskbarIcon>
</ResourceDictionary>
NotifyIconViewModel contains the RelayCommands I want to bind to
/// <summary>
/// This class contains properties that the NotifyIcon View can data bind to.
/// <para>
/// Use the <strong>mvvminpc</strong> snippet to add bindable properties to this ViewModel.
/// </para>
/// <para>
/// You can also use Blend to data bind with the tool's support.
/// </para>
/// <para>
/// See http://www.galasoft.ch/mvvm/getstarted
/// </para>
/// </summary>
public class NotifyIconViewModel : ViewModelBase
{
private ServiceController sc;
public string Welcome
{
get
{
return "Welcome to MVVM Light";
}
}
/// <summary>
/// Initializes a new instance of the NotifyIconViewModel class.
/// </summary>
public NotifyIconViewModel()
{
if (IsInDesignMode)
{
// Code runs in Blend --> create design time data.
}
else
{
sc = new ServiceController("Item");
}
}
#region Public Commands
private RelayCommand _startServiceCommand = null;
public RelayCommand StartServiceCommand
{
get
{
if (_startServiceCommand == null)
{
_startServiceCommand = new RelayCommand(
() => this.OnStartServiceCommand(),
() => (sc.Status == ServiceControllerStatus.Stopped));
}
return _stopServiceCommand;
}
}
private void OnStartServiceCommand()
{
try
{
sc.Start();
}
catch (Exception ex)
{
// notify user if there is any error
AppMessages.RaiseErrorMessage.Send(ex);
}
}
private RelayCommand _stopServiceCommand = null;
public RelayCommand StopServiceCommand
{
get
{
if (_stopServiceCommand == null)
{
_stopServiceCommand = new RelayCommand(
() => this.OnStopServiceCommand(),
() => (sc.CanStop && sc.Status == ServiceControllerStatus.Running));
}
return _stopServiceCommand;
}
}
private void OnStopServiceCommand()
{
try
{
sc.Stop();
}
catch (Exception ex)
{
// notify user if there is any error
AppMessages.RaiseErrorMessage.Send(ex);
}
}
#endregion
////public override void Cleanup()
////{
//// // Clean up if needed
//// base.Cleanup();
////}
}