I need to bind Command
and CommandParameter
to two different classes from within a WPF ContextMenu
MenuItem
that is inside an ItemsControl.ItemTemplate
. The problem is that ContextMenu
has a separate visual tree than the rest of the window. I got it working in .NET 4.0+ but can't find a workaround that works in .NET 3.5.
Note: Even the working .NET 4.0+ workaround works at runtime but breaks both the Visual Studio 2013 Designer and the Blend Designer.
How can I bind the CommandParameter
to a single CustomerViewModel
and bind the Command
to EditCustomersController
in .NET 3.5?
View
<Window
x:Class="MvvmLight1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MVVM Light Application"
DataContext="{Binding EditCustomers, Source={StaticResource Locator}}">
<ItemsControl
x:Name="rootElement"
ItemsSource="{Binding Customers}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBlock
Text="{Binding Name}">
<TextBlock.ContextMenu>
<ContextMenu
PlacementTarget="{Binding ElementName=myTextBlock}">
<MenuItem
Header="Capitalize Using RelativeSource (Does not work)"
CommandParameter="{Binding}"
Command="{Binding
Path=DataContext.CapitalizeCommand,
RelativeSource={RelativeSource
FindAncestor,
AncestorType={x:Type Window}}}" />
<MenuItem
Header="Lowercase Using RelativeSource (Does not work)"
CommandParameter="{Binding}"
Command="{Binding
Path=DataContext.LowercaseCommand,
RelativeSource={RelativeSource
FindAncestor,
AncestorType={x:Type Window}}}" />
<MenuItem
Header="Capitalize Using Source (Only works in .NET 4.0+)"
CommandParameter="{Binding}"
Command="{Binding
Path=DataContext.CapitalizeCommand,
Source={x:Reference Name=rootElement}}" />
<MenuItem
Header="Lowercase Using Source (Only works in .NET 4.0+)"
CommandParameter="{Binding}"
Command="{Binding
Path=DataContext.LowercaseCommand,
Source={x:Reference Name=rootElement}}" />
</ContextMenu>
</TextBlock.ContextMenu>
</TextBlock>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Window>
Controller
using GalaSoft.MvvmLight;
using GalaSoft.MvvmLight.Command;
using System.Collections.Generic;
using System.Windows.Input;
public class EditCustomersController : ViewModelBase
{
public EditCustomersController()
{
this.CapitalizeCommand = new RelayCommand<CustomerViewModel>(this.Capitalize);
this.LowercaseCommand = new RelayCommand<CustomerViewModel>(this.Lowercase);
var c = new List<CustomerViewModel>();
c.Add(new CustomerViewModel("Fred"));
c.Add(new CustomerViewModel("Bob"));
c.Add(new CustomerViewModel("Sue"));
c.Add(new CustomerViewModel("Sally"));
this.Customers = c;
}
public IEnumerable<CustomerViewModel> Customers { get; set; }
public ICommand CapitalizeCommand { get; private set; }
public ICommand LowercaseCommand { get; private set; }
private void Capitalize(CustomerViewModel customer)
{
customer.Name = customer.Name.ToUpper();
}
private void Lowercase(CustomerViewModel customer)
{
customer.Name = customer.Name.ToLower();
}
}
ViewModel
using GalaSoft.MvvmLight;
public class CustomerViewModel : ViewModelBase
{
private string name;
public CustomerViewModel(string name)
{
this.name = name;
}
public string Name
{
get
{
return this.name;
}
set
{
this.Set(() => this.Name, ref this.name, value);
}
}
}