MenuItem通过RelayCommand ala MVVM-Light&将所选项目传递到ViewModel。标头 [英] MenuItem Passing selected item to ViewModel via RelayCommand ala MVVM-Light & Header

查看:68
本文介绍了MenuItem通过RelayCommand ala MVVM-Light&将所选项目传递到ViewModel。标头的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我一直在努力解决这个Q / A和其他问题,但是我必须缺少一些简单的东西:
将项目绑定到MenuItem->使用Command

I've been working off of this Q/A and a bunch of others to try and figure this out, but I must be missing something simple: Bind Items to MenuItem -> use Command

我创建了这个小测试应用程序,以尝试了解上下文菜单,并了解如何连接click事件以在ViewModel中中继命令,并从上下文菜单中访问当前选定的项目。

I created this little test application to try and understand context menus and learn about how to wire up the click events to relay commands in the ViewModel, and get access to the currently selected item from the context menu.

这里是XAML:

<Window x:Class="ContextMenuTest_01.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="MainWindow" Height="105" Width="525"
    WindowStartupLocation="CenterScreen"
    xmlns:local="clr-namespace:ContextMenuTest_01.ViewModels"
    DataContext="MainWindowViewModel">
<Window.Resources>
    <ObjectDataProvider x:Key="MainWindowViewModel" ObjectType="{x:Type local:MainWindowViewModel}" IsAsynchronous="True"/>
</Window.Resources>

<!-- CONTEXT MENU -->
<Window.ContextMenu>
    <ContextMenu DataContext="MainWindowViewModel" Name="MainWindowContextMenu" PresentationTraceSources.TraceLevel="High">
        <MenuItem Header="Skins" ItemsSource="{Binding Source={StaticResource MainWindowViewModel}, Path=Skins}">
            <MenuItem.ItemContainerStyle>
                <Style TargetType="MenuItem">
                    <Setter Property="Header" Value="{Binding Source={StaticResource MainWindowViewModel}, Path=Skins.SkinName}"/>
                    <Setter Property="Command" Value="{Binding Source={StaticResource MainWindowViewModel}, Path=ContextMenuClickCommand}"/>
                    <Setter Property="CommandParameter" Value="{Binding Path=SkinName}"/>
                </Style>
            </MenuItem.ItemContainerStyle>
        </MenuItem>
    </ContextMenu>
</Window.ContextMenu>
</Window>

ViewModel:

ViewModel:

using ContextMenuTest_01.Models;
using System.Collections.ObjectModel;
using ContextMenuTest_01.CommandBase;
using System.Windows;

namespace ContextMenuTest_01.ViewModels
{
/// <summary>Main Window View Model</summary>
class MainWindowViewModel
{
    #region Class Variables
    /// <summary>The skins</summary>
    private ObservableCollection<SkinItem> skins = new ObservableCollection<SkinItem>();
    #endregion Class Variables

    public RelayCommand<object> ContextMenuClickCommand{ get; private set; }

    #region Properties
    /// <summary>Gets the skins.</summary>
    /// <value>The skins.</value>
    public ObservableCollection<SkinItem> Skins
    {
        get { return this.skins; }
        private set { this.skins = value; }
    }
    #endregion Properties

    /// <summary>Initializes a new instance of the <see cref="MainWindowViewModel"/> class.</summary>
    public MainWindowViewModel()
    {
        ContextMenuClickCommand = new RelayCommand<object>((e) => OnMenuItemClick(e));

        skins.Add(new SkinItem("Skin Item 1"));
        skins.Add(new SkinItem("Skin Item 2"));
        skins.Add(new SkinItem("Skin Item 3"));
    }

    /// <summary>Called when [menu item click].</summary>
    public void OnMenuItemClick(object selected)
    {
        MessageBox.Show("Got to the ViewModel! YAY!!!");
    }
}
}

因此OnMenuItemClick函数将得到点击调试器,如果在ViewModel中更改以下三行,则会显示消息框:

So the function OnMenuItemClick will get hit in the debugger and the message box is shown if I change the following three lines in the ViewModel:

从中继命令定义中删除:

Remove the from the Relay Command definition:

public RelayCommand ContextMenuClickCommand{ get; private set; }

从创建RelayCommand的位置删除和(e):

Remove the and (e) from where the RelayCommand is created:

ContextMenuClickCommand = new RelayCommand(() => OnMenuItemClick());

从OnMenuItemClick公共功能中删除(所选对象):

Remove the (object selected) from the OnMenuItemClick public function:

public void OnMenuItemClick()

然后一切正常,但我当然没有当前选定的项目。
那么我在XAML中缺少什么,它将通过将参数SkinName传递给RelayCommand来命令参数吗?

Then everything works but of course I don't have the currently selected item. So what am I missing in the XAML that would command parameter from passing the argument SkinName to the RelayCommand?

如果我不在线,还是这样:

Also if I leave off the line:

<Setter Property="Header" Value="{Binding Source={StaticResource MainWindowViewModel}, Path=Skins.SkinName}"/>

然后我在上下文菜单中得到以下内容:

Then I get the following in my context menu:

     Skins -> ContextMenuTest_01.Models.SkinItem

              ContextMenuTest_01.Models.SkinItem

              ContextMenuTest_01.Models.SkinItem

告诉我绑定正常,只是显示不正确,这就是为什么我试图插入

Which tells me the binding is working correctly, it's just not displaying it correctly, which is why I tried to insert the

<Setter Property="Header"....

但当然,这不像我期望的那样。

but of course that's not working as I would expect it to.

感谢您的时间!任何想法都会有所帮助!

Thanks for your time! Any ideas would be helpful!

我的代码后面没有任何内容,这就是遵循MVVM时应采取的方式。
这是我的skinItem类,没什么好说的,但是我想我会在有人问起它之前显示它:

I don't have anything in the code behind which is the way it should be when following MVVM. Here is my skinItem class, not much to speak of, but I figured I would show it before someone asks about it:

using System.Windows.Input;
using System.Windows.Media;

namespace ContextMenuTest_01.Models
{
/// <summary>A small data structure to hold a single skin item.</summary>
public class SkinItem
{
    #region Class Variables
    /// <summary>The skin name</summary>
    private string skinName;
    /// <summary>The base skin name</summary>
    private string baseSkinName;
    /// <summary>The skin path</summary>
    private string skinPath;
    /// <summary>The action to be taken when switching skins.</summary>
    private ICommand action;
    /// <summary>The icon of the skin.</summary>
    private Brush icon;
    #endregion Class Variables

    #region Constructors
    /// <summary>Initializes a new instance of the <see cref="SkinItem"/> class.</summary>
    public SkinItem() { }

    /// <summary>Initializes a new instance of the <see cref="SkinItem" /> class.</summary>
    /// <param name="newSkinName">The name of the new skin.</param>
    /// <param name="baseSkinName">Name of the base skin.</param>
    /// <param name="newSkinPath">Optional Parameter: The new skin path.</param>
    /// <param name="newSkinAction">Optional Parameter: The new skin action to be taken when switching to the new skin.</param>
    /// <param name="newSkinIcon">Optional Parameter: The new skin icon.</param>
    public SkinItem(string newSkinName, string baseSkinName = "", string newSkinPath = "", ICommand newSkinAction = null, Brush newSkinIcon = null)
    {
        if (newSkinName != "")
            this.skinName = newSkinName;

        if (baseSkinName != "")
            this.baseSkinName = baseSkinName;

        if (newSkinPath != "")
            this.skinPath = newSkinPath;

        if (newSkinAction != null)
            this.action = newSkinAction;

        if (newSkinIcon != null)
            this.icon = newSkinIcon;
    }
    #endregion Constructors

    #region Properties
    /// <summary>Gets or sets the name of the skin.</summary>
    /// <value>The name of the skin.</value>
    public string SkinName
    {
        get { return this.skinName; }
        set
        {
            if (this.skinName != value)
            {
                this.skinName = value;
                //OnPropertyChanged(() => this.SkinName);
            }
        }
    }

    /// <summary>Gets or sets the name of the base skin.</summary>
    /// <value>The name of the base skin.</value>
    public string BaseSkinName
    {
        get { return this.baseSkinName; }
        set
        {
            if (this.baseSkinName != value)
            {
                this.baseSkinName = value;
                //OnPropertyChanged(() => this.BaseSkinName);
            }
        }
    }

    /// <summary>Gets or sets the skin path.</summary>
    /// <value>The skin path.</value>
    public string SkinPath
    {
        get { return this.skinPath; }
        set
        {
            if (this.skinPath != value)
            {
                this.skinPath = value;
                //OnPropertyChanged(() => this.SkinPath);
            }
        }
    }

    /// <summary>Gets or sets the action.</summary>
    /// <value>The action.</value>
    public ICommand Action
    {
        get { return this.action; }
        set
        {
            if (this.action != value)
            {
                this.action = value;
                //OnPropertyChanged(() => this.Action);
            }
        }
    }

    /// <summary>Gets or sets the icon.</summary>
    /// <value>The icon.</value>
    public Brush Icon
    {
        get { return this.icon; }
        set
        {
            if (this.icon != value)
            {
                this.icon = value;
                //OnPropertyChanged(() => this.Icon);
            }
        }
    }
    #endregion Properties
}
}

哦,我在使用Galasoft MVVM-Light通用RelayCommand,该命令应该带有参数,可以在这里找到:
http://mvvmlight.codeplex.com/SourceControl/latest#GalaSoft。 MvvmLight / GalaSoft.MvvmLight%20%28NET35%29 / Command / RelayCommandGeneric.cs

Oh and I am using Galasoft MVVM-Light generic RelayCommand which is supposed to take parameters, can be found here: http://mvvmlight.codeplex.com/SourceControl/latest#GalaSoft.MvvmLight/GalaSoft.MvvmLight%20%28NET35%29/Command/RelayCommandGeneric.cs

推荐答案

我正在确切了解您要查找的内容有点麻烦。但是在运行代码时,我看到上下文的菜单中没有显示外观的名称。如果删除源,则标题的设置如下所示:

I'm having a little trouble understanding exactly what you're looking for. But in running your code I see that the names of the Skins are not showing in the context menu. If you remove the source so your setting looks like this for the header:

<!-- CONTEXT MENU -->
<Window.ContextMenu>
    <ContextMenu DataContext="MainWindowViewModel" Name="MainWindowContextMenu" PresentationTraceSources.TraceLevel="High">
        <MenuItem Header="Skins" ItemsSource="{Binding Source={StaticResource MainWindowViewModel}, Path=Skins}">
            <MenuItem.ItemContainerStyle>
                <Style TargetType="MenuItem">
                    <Setter Property="Header" Value="{Binding Path=SkinName}"/>
                    <Setter Property="Command" Value="{Binding Source={StaticResource MainWindowViewModel}, Path=ContextMenuClickCommand}"/>
                    <Setter Property="CommandParameter" Value="{Binding Path=SkinName}"/>
                </Style>
            </MenuItem.ItemContainerStyle>
        </MenuItem>
    </ContextMenu>
</Window.ContextMenu>

这将解决您的问题。由于要在MenuItem上设置源,因此可以更改其中各项的数据上下文。因此,您无需再次指定来源。

That will fix your problem. Since you are setting the source on the MenuItem, you change the datacontext for the items within. So you don't need to specify the source again.

编辑:

我也将路径从Skins.SkinName转换为SkinName

Also I changed the Path from Skins.SkinName to SkinName

现在我在菜单中看到项目的文本,当我单击 Skin Item 1时,在OnMenuItemClick中选择的值为皮肤项目1。

Now I see the text for the items in the menu and when I click on say "Skin Item 1", the value of selected in OnMenuItemClick is "Skin Item 1".

这篇关于MenuItem通过RelayCommand ala MVVM-Light&amp;将所选项目传递到ViewModel。标头的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆