Binding property in ContextMenu from Parent's Parent's DataContext - c#

I wanted to bind a property in a MenuItem within an ItemsControl container. I have a hierarchical model ; Item class has a list of SubItem. The ViewModel itself has a list of Item (so there's two ItemsControl, one being in the ItemTemplate of the other).
I found several other questions on SO asking about that (this one for instance) and I learned that the Visual Tree of a ContextMenu is separated from the rest.
I managed to do it and it works (but it feels kind of hacky) by "transferring" the model's data via the Tag property.
Here's the two model classes:
public class SubItem
{
public int Current { get; set; }
public Subitem(int current)
{
Current = current;
}
}
public class Item
{
public ObservableCollection<SubItem> SubItems { get; set; }
public string Parent { get; set; }
public Item(string Parent)
{
Parent = Parent;
SubItems = new ObservableCollection<SubItem>();
}
}
Here's the view model:
public class ViewModel
{
public ObservableCollection<Item> Items { get; set; }
public ViewModel()
{
Items = new ObservableCollection<Item>();
FillData();
}
private void FillData()
{
//...
}
}
And here's the ItemsControl at the root of the page (the page's DataContext is an instance of the ViewModel class):
<ItemsControl ItemsSource="{Binding Items}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<ItemsControl ItemsSource="{Binding SubItems}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Current}"
Tag="{Binding DataContext.Parent, RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}, Mode=FindAncestor}}">
<TextBlock.ContextMenu>
<ContextMenu>
<MenuItem Header="{Binding Path=PlacementTarget.Tag, RelativeSource={RelativeSource AncestorType={x:Type ContextMenu}, Mode=FindAncestor}}"/>
</ContextMenu>
</TextBlock.ContextMenu>
</TextBlock>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
My question is: is this the right way to do it? I tried many other ways to avoid binding the property to the Tag but couldn't make it work.
The ugly part is specifically:
Tag="{Binding DataContext.Parent, RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}, Mode=FindAncestor}}"
Followed by:
Header="{Binding Path=PlacementTarget.Tag, RelativeSource={RelativeSource AncestorType={x:Type ContextMenu}, Mode=FindAncestor}}"
I think it's kind of ugly and I'm sure there's a better way to do that. The solution must work with .NET 4.0.

You need to add a tag to the menu's container and bind to it using placement target.
View this example:
<StackPanel x:Key="ConfigurationListItem" x:Shared="False" Tag="{Binding ElementName=UserControl}">
<StackPanel Orientation="Horizontal">
<Button>
<Button.InputBindings>
<MouseBinding Gesture="LeftDoubleClick" Command="{Binding ElementName=UserControl, Path=LaunchCommand}" CommandParameter="{Binding}" />
<MouseBinding Gesture="LeftClick" Command="{Binding ElementName=UserControl, Path=SelectCommand}" CommandParameter="{Binding}" />
</Button.InputBindings>
</StackPanel>
<StackPanel.ContextMenu>
<ContextMenu DataContext="{Binding Path=PlacementTarget.Tag, RelativeSource={RelativeSource Self}}" Tag="{Binding}">
<MenuItem Header="Sync Environment Dependencies"
Command="{Binding Parent.PlacementTarget.Tag.SyncEnvironmentCommand, RelativeSource={RelativeSource Self}}"
CommandParameter="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ContextMenu}}, Path=PlacementTarget.DataContext}" />
</ContextMenu>
</StackPanel.ContextMenu>
</StackPanel>

Related

Bind a ContextMenu Command inside of a ItemSource to a command inside the ViewModel

I have a ItemsControl control, in which there is ContextMenu control.
The ItemsControl has its ItemsSource bound to a List<Person>.
What I want to do is to bind a DisplayNameCommand and a DisplaySurnameCommand to their corresponding context menu item, where both commands are inside of a MainWindowViewModel - not the bound Person object!!!.
The important thing is that ContextMenu needs to still have the ItemsSource data context, as I need to acces the bound object property to include it in the command parameter.
ItemsControl:
<ItemsControl ItemsSource="{Binding PeopleList}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Name}"/>
<TextBlock Text="{Binding Surname}"/>
<Image>
<Image.ContextMenu>
<ContextMenu>
<MenuItem Header="Display Name"
Command="{Binding DisplaySurnameCommand}"
CommandParameter="{Binding Name}">
</MenuItem>
<MenuItem Header="Display Surname"
Command="{Binding DisplaySurnameCommand}"
CommandParameter="{Binding Surname}">
</MenuItem>
</ContextMenu>
</Image.ContextMenu>
</Image>
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
MainWindowViewModel and Person class:
public class MainWindowViewModel
{
public MainWindowViewModel()
{
DisplayNameCommand = new RelayCommand(DisplayName);
DisplaySurnameCommand = new RelayCommand(DisplaySurname);
PeopleList = new List<Person>();
PeopleList.Add(new Person("Julie", "Adams"));
PeopleList.Add(new Person("Mack", "McMack"));
PeopleList.Add(new Person("Josh", "Broccoli"));
}
public List<Person> PeopleList { get; set; }
public void DisplayName(object message)
{
MessageBox.Show("Name: " + (string)message);
}
public void DisplaySurname(object message)
{
MessageBox.Show("Surname: "+ (string)message);
}
public RelayCommand DisplayNameCommand { get; }
public RelayCommand DisplaySurnameCommand { get; }
}
public class Person
{
public Person(string name, string surname)
{
Name = name;
Surname = surname;
}
public string Name { get; set; }
public string Surname { get; set; }
}
Also I know that it is possible to bind it to a Person object and then point it to a viewmodel command, but that's not what I'm looking for.
I've created a demo project for this problem.
What I have tried
1. Specify Command for MenuItem in a DataTemplate (accepted answer)
<ContextMenu>
<ContextMenu.ItemContainerStyle>
<Style TargetType="MenuItem">
<Setter Property="Command" Value="{Binding DataContext.DisplaySurname, RelativeSource={RelativeSource Mode=FindAncestor,AncestorType=Window}}"/>
<Setter Property="CommandParameter" Value="{Binding Name}"/>
</Style>
</ContextMenu.ItemContainerStyle>
<MenuItem Header="Display Name">
<MenuItem Header="Display Surname">
</ContextMenu>
So this is the closest that I got to the result, this does trigger the command but the problem is that there can be only 1 command set for all menu items.
If there could be a way to get around this by setting the name for the style or using something else than ItemContainerStyle it could work, but I couldn't come up with anything like that.
2. Set a relative command
<ContextMenu>
<MenuItem Header="Display Name"
Command="{Binding DataContext.DisplayNameCommand, RelativeSource={RelativeSource Mode=FindAncestor,AncestorType=Window}}"
CommandParameter="{Binding Name}"/>
<MenuItem Header="Display Surname"
Command="{Binding DataContext.DisplaySurnameCommand, RelativeSource={RelativeSource Mode=FindAncestor,AncestorType=Window}}"
CommandParameter="{Binding Surname}"/>
</ContextMenu>
This returns a binding error:
Cannot find source: RelativeSource FindAncestor, AncestorType='System.Windows.Window', AncestorLevel='1'.
3. Can't bind a ContextMenu action to a Command
The accepted answer first bound to a person object, and the updated solution didn't even work, but the second answer that I've tried:
<MenuItem Header="Display Surname"
Command="{Binding RelativeSource={RelativeSource AncestorType={x:Type Window}}, Path=DisplaySurnameCommand}"
CommandParameter="{Binding Surname}"/>
returned a binding error of:
Cannot find source: RelativeSource FindAncestor, AncestorType='System.Windows.Window', AncestorLevel='1'.
... and besides those three I've tried many more soltuions and variations, with little to no result.
I've spent a whole day trying to find a solution to this, please help me, wpf is taking away my sanity.
Ps. this is my first post so if you have any comments then let me know.
Alright, on the next day I found a workaround to this based on this solution. And it works great, the comamnds trigger, but - it changes the DataContext of the context menu , so the bound Person object is no longer available. So, to get around this, to acces the current object I've added a Person property inside of the ViewModel, that updates whenever the user clicks a image that opens a context menu. So in that way you know what item has been clicked.
This solution will work to both acces the bound object, and will work on any number of commands.
Not the preetiest solution, but it works great.
Working project demo
Xaml:
<ItemsControl ItemsSource="{Binding PeopleList}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Name}"/>
<TextBlock Text="{Binding Surname}"/>
<Image Source="more_64px.png" Tag="{Binding DataContext, RelativeSource={RelativeSource AncestorType=Window}}" >
<Image.InputBindings>
<MouseBinding Gesture="LeftClick" Command="{Binding DataContext.UpdateCurrentObjectCommand, RelativeSource={RelativeSource Mode=FindAncestor,AncestorType=Window}}" CommandParameter="{Binding}"/>
<MouseBinding Gesture="RightClick" Command="{Binding DataContext.UpdateCurrentObjectCommand, RelativeSource={RelativeSource Mode=FindAncestor,AncestorType=Window}}" CommandParameter="{Binding}"/>
</Image.InputBindings>
<Image.ContextMenu>
<ContextMenu DataContext="{Binding Path=PlacementTarget.Tag, RelativeSource={x:Static RelativeSource.Self}}">
<MenuItem Header="Display Name" Command="{Binding DisplayNameCommand}"/>
<MenuItem Header="Display Surname" Command="{Binding DisplaySurnameCommand}"/>
</ContextMenu>
</Image.ContextMenu>
</Image>
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
ViewModel with Person class:
public class MainWindowViewModel
{
public MainWindowViewModel()
{
DisplayNameCommand = new RelayCommand(DisplayName);
DisplaySurnameCommand = new RelayCommand(DisplaySurname);
PeopleList = new List<Person>();
PeopleList.Add(new Person("Julie", "Adams"));
PeopleList.Add(new Person("Mack", "McMack"));
PeopleList.Add(new Person("Josh", "Broccoli"));
UpdateCurrentObjectCommand = new RelayCommand(UpdateCurrentObject);
}
private void UpdateCurrentObject(object person)
{
CurrentPerson = (Person)person;
}
private Person CurrentPerson { get; set; }
public List<Person> PeopleList { get; set; }
public void DisplayName()
{
MessageBox.Show("Name: " + CurrentPerson.Name);
}
public void DisplaySurname()
{
MessageBox.Show("Name: " + CurrentPerson.Surname);
}
public RelayCommand DisplayNameCommand { get; }
public RelayCommand DisplaySurnameCommand { get; }
public RelayCommand UpdateCurrentObjectCommand { get; }
}
public class Person
{
public Person(string name, string surname)
{
Name = name;
Surname = surname;
}
public string Name { get; set; }
public string Surname { get; set; }
}
If you have any questions or comments, lemme know!

Bind MenuItem.IsEnabled to property of a view model

I have an MVVM WPF project with the following code:
MultiplexerVM.cs
public class MultiplexerVM : BaseViewModel
{
public ObservableCollection<MultiplexVM> Multiplexes { get; set; } = new();
public MultiplexVM SelectedMultiplex { get; set; }
public ICommand CheckAll => new CheckBoxCommand(Multiplexes);
}
MultiplexVM.cs
public class MultiplexVM : BaseViewModel
{
public bool IsChecked { get; set; }
}
MultiplexerV.xaml
<UserControl x:Class="MKVStudio.Views.MultiplexerV"
xmlns:vm="clr-namespace:MKVStudio.ViewModels"
xmlns:s="clr-namespace:System;assembly=mscorlib">
<UserControl.Resources>
<s:Boolean x:Key="True">True</s:Boolean>
<s:Boolean x:Key="False">False</s:Boolean>
</UserControl.Resources>
<Grid>
<ListView ItemsSource="{Binding Multiplexes}"
SelectedItem="{Binding SelectedMultiplex}">
<ListView.View>
<GridView>
<GridViewColumn>
<GridViewColumn.CellTemplate>
<DataTemplate>
<CheckBox IsChecked="{Binding IsChecked}"Margin="3"/>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
...
</GridView>
</ListView.View>
<ListView.ContextMenu>
<ContextMenu>
<MenuItem Command="{Binding CheckAll}"
CommandParameter="{StaticResource True}">
<MenuItem.Header>
<TextBlock Text="Check all"/>
</MenuItem.Header>
</MenuItem>
<MenuItem Command="{Binding CheckAll}"
CommandParameter="{StaticResource False}">
<MenuItem.Header>
<TextBlock Text="Uncheck all"/>
</MenuItem.Header>
</MenuItem>
</ContextMenu>
</ListView.ContextMenu>
</ListView>
</Grid>
</UserControl>
My goal is to bind IsEnabled of the context menu items to the property IsChecked of MultiplexVM.cs. The idea was to implement an IValueConverter (passing Multiplexes as value and bool as parameter). The converter returns value.Where(m => m.IsChecked == parameter).Count > 0. Essentially, when all Multiplexes are unchecked the menu item Check all is enabled and the menu item Uncheck all is disabled. The reverse thing is happening when all Multiplexes are checked. The problem here is that the converter is invoked only once when it is declared basically, and checking and unchecking the items does not trigger the converter to see what is happening.
I have tried to implement an IMultiValueConverter (but failing to use it correctly) and pass three values like this:
<MenuItem.IsEnabled>
<MultiBinding>
<Binding Source="{Binding Multiplexes.Count}" />
<Binding Source="{Binding Multiplexes}" />
<Binding Source="{StaticResource True}" /> <!--respectivly False to the other menu item-->
</MultiBinding>
</MenuItem.IsEnabled>
This doesn't work. I've tried <Binding Path="Multiplexes.Count" /> and <Binding Path="Multiplexes" />, but also doesn't work (the values passed to the converter are Unset).
Is my idea for using MultiBinding even feasible and what am I doing wrong when using it?
Why do you need to bind IsChecked to IsChecked and IsEnabled at once? This is very strange if you look at it from the Single Responsibility Principle. If you are sure that you are doing it right, you can do it like this:
<CheckBox IsChecked="{Binding IsChecked}"
IsEnabled="{Binding IsEnabled}" />
And make your class look like something like this:
public class MultiplexVM : BaseViewModel
{
public bool IsChecked
{
get => isChecked;
set
{
isChecked = value;
isEnabled = value;
RaisePropertyChanged(nameof(IsChecked));
RaisePropertyChanged(nameof(IsEnabled));
};
}
private bool isChecked;
public bool IsEnabled
{
get => isEnabled;
set
{
isChecked = value;
isEnabled = value;
RaisePropertyChanged(nameof(IsChecked));
RaisePropertyChanged(nameof(IsEnabled));
};
}
private bool isChecked;
}
From what I understand, you want to make an object bound to a "parent" (MenuItem => MultiplexerVM) be dependant on a property of its child collection (CheckBox => MultiplexVM.IsChecked, which is an item in MultiplexerVM.Multiplexes)
In this scenario, a child has to be somehow aware of its parent (when the child changes, it has to "push" the change up to the parent; in other words, the parent has to be informed when the change happens).
I can think of two ways to do it:
on the VM level: in every MultiplexVM, set a reference to the parent view model or collection, then you can update the CanCheckAll / CanUncheckAll functionality (however you implement it) every time the child's IsChecked changes (tedious; I suppose you can also do this with events, but attaching PropertyChanged handler to every child item is also a bit much)
cheat a bit by using the GUI level: you can update the CanCheckAll / CanUncheckAll functionality whenever the CheckBox is clicked
Below is an example of how you can implement the 2nd version.
In your MultiplexerVM:
public bool CanCheckAll => Multiplexes.Any(a => !a.IsChecked);
public bool CanUncheckAll => Multiplexes.Any(a => a.IsChecked);
public void RefreshCheckUncheckAll()
{
NotifyPropertyChanged(nameof(CanCheckAll));
NotifyPropertyChanged(nameof(CanUncheckAll));
}
Then, call RefreshCheckUncheckAll() in CheckAll command implementation and in:
private void CheckBox_Click(object sender, RoutedEventArgs e)
{
((MultiplexerVM)this.DataContext).RefreshCheckUncheckAll();
}
Then, the xaml will look something like this:
<ListView ItemsSource="{Binding Multiplexes}" SelectedItem="{Binding SelectedMultiplex}">
<ListView.ContextMenu>
<ContextMenu>
<MenuItem
Command="{Binding CheckAll}"
CommandParameter="{StaticResource True}"
IsEnabled="{Binding CanCheck}">
<MenuItem.Header>
<TextBlock Text="Check all" />
</MenuItem.Header>
</MenuItem>
<MenuItem
Command="{Binding CheckAll}"
CommandParameter="{StaticResource False}"
IsEnabled="{Binding CanUncheck}">
<MenuItem.Header>
<TextBlock Text="Uncheck all" />
</MenuItem.Header>
</MenuItem>
</ContextMenu>
</ListView.ContextMenu>
<ListView.View>
<GridView>
<GridViewColumn>
<GridViewColumn.CellTemplate>
<DataTemplate>
<TextBlock Margin="3" Text="{Binding Name}" />
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn>
<GridViewColumn.CellTemplate>
<DataTemplate>
<CheckBox Margin="3" IsChecked="{Binding IsChecked}" Click="CheckBox_Click" />
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
</GridView>
</ListView.View>
</ListView>

WPF Context Menu command Binding

I have tried few solutions given in SO, but still i'm unable to trigger the command.
XAML:
<Image Source="{Binding CurrentImage.Source, Mode=OneWay}" Grid.Row="0" Grid.Column="1" Tag="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType= Window}}">
<Image.ContextMenu>
<ContextMenu DataContext="{Binding PlacementTarget.Tag, RelativeSource={RelativeSource Self}}">
<MenuItem Header="Edit Image" Command="{Binding PlacementTarget.Tag.EditImageCommand, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=ContextMenu}}"></MenuItem>
</ContextMenu>
</Image.ContextMenu>
ViewModel:
private ICommand _EditImageCommand;
public ICommand EditImageCommand
{
get
{
return _EditImageCommand ?? (_EditImageCommand = new CommandHandler(() => EditImage(), _canExecute));
}
}
public void EditImage()
{
}
Change:
private ICommand _EditImageCommand;
private ICommand EditImageCommand
{
get
{
return _EditImageCommand ?? (_EditImageCommand = new CommandHandler(() => EditImage(), _canExecute));
}
}
public void EditImage()
{
}
to
private ICommand _EditImageCommand;
public ICommand EditImageCommand // has to be public
{
get
{
return _EditImageCommand ?? (_EditImageCommand = new CommandHandler(() => EditImage(), _canExecute));
}
}
public void EditImage()
{
}
Commands have to be public to be accessed (or internal for the sake of correctness).
Also, change your xaml to:
<Image Source="{Binding CurrentImage.Source, Mode=OneWay}" Grid.Row="0" Grid.Column="1" Tag="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType= Window}}">
<Image.ContextMenu>
<ContextMenu DataContext="{Binding PlacementTarget.Tag, RelativeSource={RelativeSource Self}}">
<MenuItem Header="Edit Image" Command="{Binding EditImageCommand, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=ContextMenu}}"></MenuItem>
</ContextMenu>
</Image.ContextMenu>
Have changed my XAML to,
<Window.Resources>
<local:ImageList x:Key="SliderViewModel"></local:ImageList>
</Window.Resources>
<Image Source="{Binding CurrentImage.Source, Mode=OneWay}" Grid.Row="0" Grid.Column="1">
<Image.ContextMenu>
<ContextMenu>
<MenuItem Header="Edit Image" Command="{Binding EditImageCommand, Source={StaticResource SliderViewModel}}"></MenuItem>
</ContextMenu>
</Image.ContextMenu>
</Image>
Working fine. Thanks
Another nice workaround is to declare a static instance of your view model in App.xaml: <ViewModelTypeName x:Key="ViewModelName" d:IsDataSource="True" />and bind like this Command="{Binding Source={StaticResource ViewModelName}, Path=MyCommand}" I had the same issue when i needed to bind from Window and menuitem's native DataContext simultaneously. Also this solution looks not that complicated.

Cannot Access Command in WPF UserControl's DataGrid Row

I am not able to execute Command (TestCommand) in user control may be because DataGrid of usercontrol using FileDetailsList(List)
The following is the wpf form and i am using MVVM
<Window>
<TabControl>
<TabItem Header="Result">
<USERCONTROL:FileSearchResult></USERCONTROL:FileSearchResult>
</TabItem>
</TabControl>
</Window>
The below is the user control
<UserControl>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*"></RowDefinition>
<RowDefinition Height="30"></RowDefinition>
</Grid.RowDefinitions
<DataGrid Grid.Row="0" ItemSource="{Binding FileDetailsList}">
<DataGrid.Columns>
<DataGridTemplateColumn>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Button>View
<Button.ContextMenu>
<ContextMenu FontSize="11">
<MenuItem Command="{Binding TestCommand}" CommandParameter="{Binding FileId}" Header="Splitter Errors"/>
</ContextMenu>
</Button.ContextMenu>
</Button>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTextColumn Binding="{Binding FileId}" Header="File ID"/>
<DataGridTextColumn Binding="{Binding FileName}" Header="File Name"/>
<DataGrid.Columns>
<DataGrid Grid.Row="0">
</Grid>
</UserControl>
The below is the view model
public class FileDetailsViewModel : INotifyPropertyChanged
{
private List<FileDetail> _fileDetailsList = new List<FileDetail>();
public RelayCommand<Int32> TestCommand { get; private set; }
public FileDetailsViewModel()
{
TestCommand = new RelayCommand<int>(OpenTestCommand);
}
private void OpenTestCommand(int fileId)
{
///Some code
}
public List<FileDetail> FileDetailsList { get { return _fileDetailsList; } set { _fileDetailsList = value; NotifyPropertyChanged("FileDetailsList"); } }
public event PropertyChangedEventHandler PropertyChanged;
protected void NotifyPropertyChanged(string property)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(property));
}
}
}
Please help solve this issue
you try this it will work 100%,
<Button Tag="{Binding Path=DataContext, RelativeSource={RelativeSource AncestorType={x:Type DataGrid}}}">
View
<!-- Binding RelativeSource={RelativeSource AncestorType={x:Type DataGrid}}, Path=DataContext. -->
<Button.ContextMenu>
<ContextMenu FontSize="11">
<MenuItem Command="{Binding Path=PlacementTarget.Tag.TestCommand,
RelativeSource={RelativeSource AncestorType={x:Type ContextMenu}}}"
CommandParameter="{Binding FileId}"
Header="Splitter Errors" />
</ContextMenu>
</Button.ContextMenu>
</Button>
use menuitem command binding like this,
<MenuItem Command="{Binding DataContext.TestCommand, RelativeSource={RelativeSource AncestorType=DataGrid }}"
WPF rule number 1:
When binding does not work, check your output window!
you would see error saying, that TestCommand does not exist in FileDetails. This is because DataContext of each row in DataGrid and all descendant elements including your ContextMenu is not FileDetailsViewModel but FileDetail
easiest workaround is to move TestCommand to FileDetail class.
alternativelly, you can modify the binding to point to FileDetailsViewModel. Give name to root element in your UserControl or Window, e.g: LayoutRoot. Then use ElementName in the binding:
<MenuItem Command="{Binding LayoutRoot.DataContext.TestCommand, ElementName=LayoutRoot}"
CommandParameter="{Binding FileId}"
Initialized="ContextMenu_Initialized"/>
private void ContextMenu_Initialized(object sender, EventArgs e)
{
NameScope.SetNameScope((ContextMenu)sender, NameScope.GetNameScope(this));
}
another way how to fix the binding is workaround proposed by #jobyjames85, but it is little hacky. On the other side it is plain xaml solution with no codebehind involved

Delete item from ItemControl

My class is has a ObservableCollection of my viewmodel class and I set the itemsource of the Itemcontrol in xaml as below
<ItemsControl ItemsSource="{Binding ConditionItems}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Expander Background="#FFD0D7EB">
<StackPanel>
<Button Content="Delete" HorizontalAlignment="Right" Width="180" Margin="0,0,12,10" Command="{Binding DeleteItem}" CommandParameter="{Binding}">
</Button> </StackPanel>
</Expander>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
For some reason my DeleteItem is never called.
private RelayCommand _DeleteRule;
private void DoDeleteRule(object item)
{
if (item != null)
{
MessageBox.Show("in del");
}
}
public ICommand DeleteItem
{
get
{
if (_DeleteRule == null)
_DeleteRule = new RelayCommand(o => DoDeleteRule(o));
return _DeleteRule;
}
}
Am I doing anything wrong in xaml?
The ItemsControl is bound using {Binding ConditionItems}, so it expects the DeleteItem command to be inside the subitems of that list. I guess this is not the case, the DeleteItem exists on the ViewModel.
You could bind to the DataContext of the Window for example, where you can find the DeleteItem command. Or create a proxy element.
I found it. My xaml should be
<Button Content="Delete" Command="{Binding DataContext.DeleteItem,
RelativeSource={RelativeSource FindAncestor,
AncestorType={x:Type ItemsControl}}}" CommandParameter="{Binding}">
</Button>

Categories

Resources