[英]Binding command of parent control to ContextMenu MenuItem
我在做什么:
在我的 DataGrid 上,我有一个上下文菜单来添加模板项。 MenuItem 是使用父 MenuItem 的 ItemsSource 属性动态创建的。 ItemsSource 是我的模板对象的 ObservableCollection。 我想从集合对象属性中获取动态 MenuItems 的 Header,但从我的主 ViewModel 执行命令。 主 ViewModel 绑定到根 Grid 的 DataContext。
我的问题:
MenuItems 已正确创建,我还可以将标题绑定到集合中对象的属性。 但是命令绑定不起作用。 我可以在输出窗口中看到一个错误:“System.Windows.Data 错误:4:无法找到与引用绑定的源...”
这是我的代码简化为问题(使用 MVVM 灯):
主窗口.xaml:
<Window x:Class="TapTimesGui.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
Title="MainWindow" Height="600" Width="1000">
<!-- root grid -->
<Grid x:Name="RootGrid" DataContext="{Binding Source={StaticResource Locator}, Path=Main}">
<!-- Jobs overview -->
<DataGrid Grid.Column="0">
<DataGrid.ContextMenu>
<ContextMenu>
<!-- following works fine -->
<MenuItem Header="Basic command test" Command="{Binding AddTemplateJobCommand}" CommandParameter="Basic command test"/>
<MenuItem Header="Add template..." ItemsSource="{Binding JobTemplates}">
<MenuItem.ItemTemplate>
<DataTemplate>
<!-- following command does not work System.Windows.Data Error -->
<MenuItem Header="{Binding Name}" Command="{Binding ElementName=RootGrid, Path=DataContext.AddTemplateJobCommand}" CommandParameter="{Binding Name}"/>
</DataTemplate>
</MenuItem.ItemTemplate>
<!--<MenuItem.ItemContainerStyle> also does not work
<Style TargetType="MenuItem">
<Setter Property="Header" Value="{Binding Name}"></Setter>
<Setter Property="Command" Value="{Binding ElementName=RootGrid, Path=DataContext.SaveDayToNewFileCommand}"></Setter>
</Style>
</MenuItem.ItemContainerStyle>-->
</MenuItem>
</ContextMenu>
</DataGrid.ContextMenu>
</DataGrid>
</Grid>
</Window>
主窗口.xaml.cs:
using System.Windows;
namespace TapTimesGui
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
}
}
主视图模型.cs:
using GalaSoft.MvvmLight;
using GalaSoft.MvvmLight.CommandWpf;
using GalaSoft.MvvmLight.Messaging;
using System.Collections.ObjectModel;
using System.Windows.Input;
using TapTimesGui.Services;
namespace TapTimesGui.ViewModel
{
/// <summary>
/// ...
/// </summary>
public class MainViewModel : ViewModelBase
{
#region Properties and backing fields
/// <summary>
/// Templates for jobs
/// </summary>
public ObservableCollection<JobTemplate> JobTemplates
{
get
{
return _jobTemplates;
}
}
private readonly ObservableCollection<JobTemplate> _jobTemplates = new ObservableCollection<JobTemplate>();
#endregion Properties and backing fields
#region ICommand properties
public ICommand AddTemplateJobCommand { get; private set; }
#endregion ICommand properties
#region Constructors
/// <summary>
/// Initializes a new instance of the MainViewModel class.
/// </summary>
public MainViewModel()
{
// assign commands
AddTemplateJobCommand = new RelayCommand<string>(AddTemplateJob);
// populate data on start
if (IsInDesignMode)
{
// Code runs in Blend --> create design time data.
}
else
{
//TODO test for templates
JobTemplate tmpJobTemplate = new JobTemplate();
tmpJobTemplate.Name = "Template 1";
tmpJobTemplate.Template = "TestCustomer1 AG";
JobTemplates.Add(tmpJobTemplate);
tmpJobTemplate = new JobTemplate();
tmpJobTemplate.Name = "Template 2";
tmpJobTemplate.Template = "TestCustomer2 AG";
JobTemplates.Add(tmpJobTemplate);
}
}
#endregion Constructors
#region Command methods
private void AddTemplateJob(string name)
{
//TODO implement
Messenger.Default.Send<NotificationMessage>(new NotificationMessage(name));
}
#endregion Command methods
}
}
ViewModelLocator.cs:
/*
In App.xaml:
<Application.Resources>
<vm:ViewModelLocator xmlns:vm="clr-namespace:TapTimesGui"
x:Key="Locator" />
</Application.Resources>
In the View:
DataContext="{Binding Source={StaticResource Locator}, Path=ViewModelName}"
You can also use Blend to do all this with the tool's support.
See http://www.galasoft.ch/mvvm
*/
using GalaSoft.MvvmLight;
using GalaSoft.MvvmLight.Ioc;
//using Microsoft.Practices.ServiceLocation; TODO http://www.mvvmlight.net/std10 chapter known issues
using CommonServiceLocator;
using GalaSoft.MvvmLight.Messaging;
using System;
using System.Windows;
namespace TapTimesGui.ViewModel
{
/// <summary>
/// This class contains static references to all the view models in the
/// application and provides an entry point for the bindings.
/// </summary>
public class ViewModelLocator
{
/// <summary>
/// Initializes a new instance of the ViewModelLocator class.
/// </summary>
public ViewModelLocator()
{
ServiceLocator.SetLocatorProvider(() => SimpleIoc.Default);
////if (ViewModelBase.IsInDesignModeStatic)
////{
//// // Create design time view services and models
//// SimpleIoc.Default.Register<IDataService, DesignDataService>();
////}
////else
////{
//// // Create run time view services and models
//// SimpleIoc.Default.Register<IDataService, DataService>();
////}
SimpleIoc.Default.Register<MainViewModel>();
Messenger.Default.Register<NotificationMessage>(this, NotificationMessageHandler);
Messenger.Default.Register<Exception>(this, ExceptionMessageHandler);
}
public MainViewModel Main
{
get
{
return ServiceLocator.Current.GetInstance<MainViewModel>();
}
}
public static void Cleanup()
{
// TODO Clear the ViewModels
}
private void NotificationMessageHandler(NotificationMessage message)
{
//MessageBox.Show(message.Notification);
System.Diagnostics.Debug.WriteLine(message.Notification);
MessageBox.Show(message.Notification);
}
private void ExceptionMessageHandler(Exception ex)
{
MessageBox.Show(ex.ToString(), "Exception", MessageBoxButton.OK, MessageBoxImage.Error);
}
}
}
作业模板.cs:
using System.ComponentModel;
namespace TapTimesGui.Services
{
/// <summary>
/// Named TapTimesGui.Model.Job template
/// </summary>
/// <remarks>Can be used e.g. to save Templates of specific objects</remarks>
public class JobTemplate : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public string Name
{
get
{
return _name;
}
set
{
_name = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Name)));
}
}
private string _name = "";
public string Template //Type was not string originally, it was if my Model object
{
get
{
//TODO proper cloning
return _template;
}
set
{
//TODO proper cloning
_template = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Template)));
}
}
private string _template = "Template";
}
}
版本:
我已经尝试寻找解决方案有一段时间了。 在此先感谢您的帮助。
您也可以访问ItemTemplate
内的ViewModelLocator
。
<DataGrid Grid.Column="0">
<DataGrid.ContextMenu>
<ContextMenu>
<MenuItem Header="Add template..." ItemsSource="{Binding JobTemplates}">
<MenuItem.ItemTemplate>
<DataTemplate>
<MenuItem Header="{Binding Name}" Command="{Binding Source={StaticResource Locator}, Path=Main.AddTemplateJobCommand}" CommandParameter="{Binding Name}"/>
</DataTemplate>
</MenuItem.ItemTemplate>
</MenuItem>
</ContextMenu>
</DataGrid.ContextMenu>
</DataGrid>
没有 MVVM light 的更通用方法可能是使用绑定代理。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.