2011-03-31 24 views
1

我有一個在ResourceDictionary中定義的NotifyIcon的WPF應用程序,該應用程序已添加到App start上的Application.Current.Resources。WPF - 將GlobalResource耦合到ViewModel

我正在使用MVVM-Light框架,我想將NotifyIcon上的ContextMenu.MenuItems的Command屬性綁定到ViewModel中定義的公共RelayCommand。

我很喜歡將視圖耦合到ViewModel,但是如何將全局資源耦合到ViewModel?

這是我在得到這個工作,只是不知道如果我在正確的線路或未遂......

當我運行這段代碼,我得到一個錯誤說「無法找到資源名爲' Locator'。資源名稱區分大小寫。「這從在DataContext在NotificationIconResources.xaml在TaskBarIcon標籤綁定起源

SingleInstanceManager確保只有一個實例可以創建

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取代的App.xaml和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是定義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包含我要綁定到

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

回答

0

既然你已經聲明在應用程序級別NotifyIcon的的RelayCommands,你不會要能夠把它繼承另一種觀點的視圖模型,因爲它不是在任何視圖的視覺樹中。最好的辦法是給NotifyIcon自己的ViewModel定義命令,然後處理ViewModels之間的通信,而不是通過UI。

如果你需要它綁定到一個特定視圖的ViewModel,你也可以考慮在該視圖中聲明它而不是全局的,在這種情況下,它可以自動繼承你正在嘗試使用的DataContext(但它會被打開,以該觀點結束)。

+0

你好約翰,感謝您的答覆。道歉 - 在回答我的問題時,我一定會添加更多細節。我想我已經得到了這個地方,它也遵循你的建議,但是我陷入困境 - 任何機會,你可以查看我剛剛添加的代碼? – Drammy 2011-03-31 12:35:47

0

謝謝你的幫助約翰。

我已經解決了這個問題。

我刪除以下行從ControllerApp.cs

ViewModel.ViewModelLocator vmLocator = new ViewModel.ViewModelLocator(); 
    Application.Current.Resources.Add("Locator", vmLocator); 

並增加ViewModelLocator到資源字典(NotificationIconResources.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:vm="clr-namespace:Controller.ViewModel"> 

<vm:ViewModelLocator x:Key="Locator"/> 


    <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="{Binding Path=Item}" /> 
       <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>