gpt4 book ai didi

c# - MenuItem 通过 RelayCommand ala MVVM-Light & Header 将所选项目传递给 ViewModel

转载 作者:行者123 更新时间:2023-12-03 10:37:17 29 4
gpt4 key购买 nike

我一直在研究这个 Q/A 和一堆其他人来尝试解决这个问题,但我一定遗漏了一些简单的东西:
Bind Items to MenuItem -> use Command

我创建了这个小测试应用程序来尝试理解上下文菜单,并了解如何连接单击事件以在 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>

View 模型:
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!!!");
}
}
}

因此,如果我在 ViewModel 中更改以下三行,则 OnMenuItemClick 函数将在调试器中被命中,并且会显示消息框:

从中继命令定义中删除:
public RelayCommand ContextMenuClickCommand{ get; private set; }

从创建 RelayCommand 的位置删除和 (e):
ContextMenuClickCommand = new RelayCommand(() => OnMenuItemClick());

从 OnMenuItemClick 公共(public)函数中删除 (object selected):
public void OnMenuItemClick()

然后一切正常,但当然我没有当前选定的项目。
那么我在 XAML 中遗漏了什么,将命令参数从参数 SkinName 传递给 RelayCommand ?

另外,如果我下线:
<Setter Property="Header" Value="{Binding Source={StaticResource MainWindowViewModel}, Path=Skins.SkinName}"/>

然后我在上下文菜单中得到以下内容:
     Skins -> ContextMenuTest_01.Models.SkinItem

ContextMenuTest_01.Models.SkinItem

ContextMenuTest_01.Models.SkinItem

这告诉我绑定(bind)工作正常,只是没有正确显示,这就是为什么我试图插入
<Setter Property="Header"....

但当然这并没有像我期望的那样工作。

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

我在后面的代码中没有任何内容,这是遵循 MVVM 时应该采用的方式。
这是我的 skinItem 类,没什么好说的,但我想我会在有人问起它之前展示它:
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

最佳答案

我很难准确理解您要查找的内容。但是在运行您的代码时,我看到皮肤的名称没有显示在上下文菜单中。如果您删除源代码,则您的标题设置如下所示:

<!-- 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 上设置源,因此您更改了其中项目的数据上下文。所以你不需要再次指定来源。

编辑:

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

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

关于c# - MenuItem 通过 RelayCommand ala MVVM-Light & Header 将所选项目传递给 ViewModel,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21585828/

29 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com