WPF - Соединение GlobalResource с ViewModel - PullRequest
1 голос
/ 31 марта 2011

У меня есть приложение WPF с NotifyIcon , определенным в ResourceDictionary, который добавляется в Application.Current.Resources при запуске приложения.

Я использую инфраструктуру MVVM-Light, и яхочу связать свойства Command ContextMenu.MenuItems в NotifyIcon с общедоступной RelayCommand, определенной в ViewModel.

Мне удобно связывать View с ViewModel, но как связать глобальный ресурс с ViewModel?

Вот моя попытка заставить это работать, просто не уверен, что я на правильных линиях или нет ...

Когда я запускаю этот код, я получаю сообщение об ошибке "Не удается найти ресурс с именем 'Locator'. Имена ресурсов чувствительны к регистру. "Это происходит из-за привязки DataContext к тегу TaskBarIcon в NotificationIconResources.xaml

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 содержит RelayCommands, которые я хочу привязать к

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

Ответы [ 2 ]

0 голосов
/ 31 марта 2011

Спасибо за вашу помощь, Джон.

Я решил проблему.

Я удалил следующие строки из ControllerApp.cs

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

И добавил ViewModelLocatorв ResourceDictionary (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>
0 голосов
/ 31 марта 2011

Учитывая, что вы объявили NotifyIcon на уровне приложения, вы не сможете получить его от наследования ViewModel другого представления, поскольку оно не находится в визуальном дереве каких-либо представлений. Лучше всего, вероятно, предоставить NotifyIcon свой собственный ViewModel с определенными для него командами, а затем обрабатывать связь между ViewModel, а не через пользовательский интерфейс.

Если вам нужно привязать его к ViewModel определенного представления, вы также можете рассмотреть возможность объявления его в этом представлении вместо глобального, в этом случае он может автоматически наследовать DataContext, который вы пытаетесь использовать (но он будет открыт и закрыт). с этой точки зрения).

...