C# 菜单项通过RelayCommand ala MVVM Light&;标题

C# 菜单项通过RelayCommand ala MVVM Light&;标题,c#,wpf,xaml,mvvm,contextmenu,C#,Wpf,Xaml,Mvvm,Contextmenu,我一直在研究这个Q/A和其他一些问题,试图解决这个问题,但我肯定错过了一些简单的东西: 我创建了这个小测试应用程序来尝试和理解上下文菜单,并了解如何将单击事件连接到ViewModel中的中继命令,以及如何从上下文菜单访问当前选定的项 以下是XAML: <Window x:Class="ContextMenuTest_01.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

我一直在研究这个Q/A和其他一些问题,试图解决这个问题,但我肯定错过了一些简单的东西:

我创建了这个小测试应用程序来尝试和理解上下文菜单,并了解如何将单击事件连接到ViewModel中的中继命令,以及如何从上下文菜单访问当前选定的项

以下是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>
从创建RelayCommand的位置删除和(e):

ContextMenuClickCommand = new RelayCommand(() => OnMenuItemClick());
从OnMenuItemClick公用函数中删除(选定对象):

public void OnMenuItemClick()
然后一切正常,但我当然没有当前选择的项目。 那么,我在XAML中缺少了什么,它将命令参数从参数SkinName传递到RelayCommand

此外,如果我不在线:

<Setter Property="Header" Value="{Binding Source={StaticResource MainWindowViewModel}, Path=Skins.SkinName}"/>
这告诉我绑定工作正常,只是显示不正确,这就是为什么我尝试插入

<Setter Property="Header"....
this.SkinName);
}
}
}
///获取或设置基础外观的名称。
///基础蒙皮的名称。
公共字符串BaseSkinName
{
获取{返回this.baseSkinName;}
设置
{
if(this.baseSkinName!=值)
{
this.baseSkinName=值;
//OnPropertyChanged(()=>this.BaseSkinName);
}
}
}
///获取或设置外观路径。
///皮肤路径。
公共字符串路径
{
获取{返回this.skinPath;}
设置
{
if(this.skinPath!=值)
{
this.skinPath=值;
//OnPropertyChanged(()=>this.SkinPath);
}
}
}
///获取或设置操作。
///行动。
公开命令行动
{
获取{返回this.action;}
设置
{
if(this.action!=值)
{
这个动作=价值;
//OnPropertyChanged(()=>this.Action);
}
}
}
///获取或设置图标。
///图标。
公共画笔图标
{
获取{返回this.icon;}
设置
{
if(this.icon!=值)
{
this.icon=值;
//OnPropertyChanged(()=>this.Icon);
}
}
}
#端域属性
}
}
哦,我使用的是Galasoft MVVM Light generic RelayCommand,它应该带有参数,可以在这里找到:

我不太清楚你到底在找什么。但是在运行代码时,我看到上下文菜单中没有显示皮肤的名称。如果删除源,则标题的设置如下所示:

<!-- 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上设置源,因此需要更改其中项目的datacontext。因此,无需再次指定源

编辑:

我还将路径从Skins.SkinName更改为SkinName


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

因此,SkinName没有显示在上下文菜单中是一个问题吗?是的,而且我无法向relayCommand发送命令参数。因此,这实际上是一个由两部分组成的问题。我说过删除它将获得一些要显示的数据,但它只显示ContextMenuTest_01.Models.SkinItem而不是实际名称,这就是我试图添加此header属性的原因,这并不能解决命令参数发送到RelayCommand的问题。在我的测试中,我所要做的全部更改是,我的菜单项显示Skin Item 1、Skin Item 2和Skin Item 3Oops,而且我忘了提到我将Skins.SkinName的路径更改为SkinName。我确实尝试过,上下文菜单显示了3行,但它们对我来说都是空的,并且从不触发中继命令。等等,您在谈论将Setter属性=“Header”Path=Skins更改为Path=SkinName?如果我换了,也可以
     Skins -> ContextMenuTest_01.Models.SkinItem

              ContextMenuTest_01.Models.SkinItem

              ContextMenuTest_01.Models.SkinItem
<Setter Property="Header"....
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
}
}
<!-- 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>