MenuItem + CommandParameter = null ! Why? - c#

I trying to pass a parameter SelectedItems to command, but parameter is always null.
<sd:SharpTreeView Name="MyTreeView" Margin="10,38,120,10" Root="{Binding Tests}" ShowAlternation="True" SelectionMode="Multiple">
<sd:SharpTreeView.ContextMenu>
<ContextMenu>
<MenuItem
Header="Copy"
CommandParameter="{Binding ElementName=MyTreeView, Path=SelectedItems}"
Command="{Binding CopySelectedTests}" />
</ContextMenu>
</sd:SharpTreeView.ContextMenu>
<ListView.View>
...
I new in WPF, so for me it is was not helpful to read answers from here and from others questions. It always remain null.
I have tried it not in ContextMenu and it works fine, but it is not what I want.
EDIT 1
I tried this:
CommandParameter="{Binding Path=UIElement.(sd:SharpTreeView.SelectedItems), RelativeSource={RelativeSource AncestorType={x:Type ContextMenu}}}"
this:
CommandParameter="{Binding Path=SelectedItems, RelativeSource={RelativeSource Mode=FindAncestor, AncestorLevel=1, AncestorType={x:Type ContextMenu}}}"
and many other, it is always null.
It only does not null for this:
CommandParameter="{Binding}"
But it is not what I need.

I finally solved it:
CommandParameter="{Binding
Path=PlacementTarget.SelectedItems,
RelativeSource=
{RelativeSource FindAncestor, AncestorType={x:Type ContextMenu}}
}"
The important part was to specify PlacementTarget.

Related

Bind ContextMenu Item inside Button to ViewModel

I'm trying to add a ContextMenu with an ItemMenu to a button
<Button x:Name="RemoteMachine" Command="{Binding ElementName=RemoteMachines, Path=DataContext.RemoteMachineSelectedCommand}" CommandParameter="{Binding}">
<Button.ContextMenu>
<ContextMenu>
<MenuItem Header="Delete" CommandParameter="{Binding}"
Command="{Binding ElementName=RemoteMachines, Path=DataContext.DeleteRemoteMachineCommand}"/>
</ContextMenu>
</Button.ContextMenu>
</Button>
In my model I have the following
public ICommand RemoteMachineSelectedCommand => new CommandHandler(p => MachineSelectedAction(p, true), true);
public ICommand DeleteRemoteMachineCommand => new CommandHandler(p => DeleteRemoteMachineAction(p), true);
The Button command works correctly while the ContextMenu one doesn't.
I suppose I'm binding it someway wrong.
Any suggestion?
You could bind the Tag property of the Button to the command and then bind to it from the MenuItem using the PlacementTarget property of the parent ContextMenu:
<Button x:Name="RemoteMachine"
Tag="{Binding ElementName=RemoteMachines, Path=DataContext.DeleteRemoteMachineCommand}"
Command="{Binding ElementName=RemoteMachines, Path=DataContext.RemoteMachineSelectedCommand}"
CommandParameter="{Binding}">
<Button.ContextMenu>
<ContextMenu>
<MenuItem Header="Delete"
CommandParameter="{Binding PlacementTarget.CommandParameter,
RelativeSource={RelativeSource AncestorType=ContextMenu}}"
Command="{Binding PlacementTarget.Tag,
RelativeSource={RelativeSource AncestorType=ContextMenu}}"/>
</ContextMenu>
</Button.ContextMenu>
</Button>

Adding new element to a ComboBox which is in a DataGrid in WPF using MVVM

I have a DataGrid which contains Transactions. I have an InterestOrDividend column where I can select a value using a ComboBox. This works fine.
A new feature would be to enter a value and add it to the list of possibilities. I set IsEditable to true and added Interaction.Triggers from http://schemas.microsoft.com/expression/2010/interactivity
Problem 1:
It seems InterestOrDividendSelectionChangedCommand not only fires when the selection is changed but also when I scroll the DataGrid and such rows come into view which has a not null value in the InterestOrDividend column. Moreover, when a new value is entered (which is not in the list), the event does not fire.
Problem 2:
I want to bind the Text property of the ComboBox to get the newly added value. It seems the event fire before the Text property changes, so I get the old value.
<DataGridTemplateColumn Header="{x:Static r:Resource.InterestOrDividend}"
CellTemplate="{StaticResource InterestOrDividendEditingTemplate}"
CellEditingTemplate="{StaticResource InterestOrDividendEditingTemplate}" />
<DataTemplate x:Key="InterestOrDividendEditingTemplate">
<ComboBox ItemsSource="{Binding RelativeSource={RelativeSource FindAncestor,
AncestorType={x:Type DataGrid}}, Path=DataContext.AppData.AlienTypeObjects}"
SelectedItem="{Binding InterestOrDividend}"
DisplayMemberPath="FullName"
IsEditable="True"
<i:Interaction.Triggers>
<i:EventTrigger EventName="SelectionChanged">
<i:InvokeCommandAction Command="{Binding RelativeSource={RelativeSource FindAncestor,
AncestorType={x:Type DataGrid}},
Path=DataContext.InterestOrDividendSelectionChangedCommand}"
CommandParameter="{Binding RelativeSource={RelativeSource FindAncestor,
AncestorType={x:Type ComboBox}}, Path=Text}"
/>
</i:EventTrigger>
</i:Interaction.Triggers>
</ComboBox>
</DataTemplate>
This is my solution. Instead of using the EventTrigger I catch the new element in the setter of NewInterestOrDividend. It is important that the UpdateSourceTrigger is LostFocus. When InterestOrDividend is null and you change the focus then value in NewInterestOrDividend contains the new value.
<DataTemplate x:Key="InterestOrDividendEditingTemplate">
<ComboBox ItemsSource="{Binding RelativeSource={RelativeSource FindAncestor,
AncestorType={x:Type DataGrid}}, Path=DataContext.AppData.AlienTypeObjects}"
DisplayMemberPath="FullName" Style="{StaticResource ComboBoxError}"
IsEditable="True"
SelectedItem="{Binding InterestOrDividend, UpdateSourceTrigger=LostFocus}"
Text="{Binding NewInterestOrDividend, UpdateSourceTrigger=LostFocus}">
</ComboBox>
</DataTemplate>

WPF Datagrid ContextMenu SelectedItem

I have the following problem.
I have a DataGrid with a ContextMenu. But when the Command is triggered the SelectedItem is always null. Any recommendations?
Thats my ContextMenu:
<DataGrid.ContextMenu>
<ContextMenu>
<MenuItem Command="{Binding OpenFileCommand}"
CommandParameter="{Binding Path=SelectedItem,
RelativeSource={RelativeSource AncestorType={x:Type DataGrid}}}"
Header="open file" />
i also tried:
CommandParameter="{Binding ElementName=nameOfGrid, Path=SelectedItem}"
Thanks for your help. I fixed it this way: CommandParameter="{Binding PlacementTarget.SelectedItem,
RelativeSource={RelativeSource FindAncestor,
AncestorType={x:Type ContextMenu}}}"
I think your answer is here: CommandParameters in ContextMenu in WPF
Another better option imho is to keep "SelectedItem" binded in your ViewModel.
Then you don't need a command parameter anymore, and you can juste use the SelectedItem binded from your ViewModel.
The problem is that the ContextMenu is in a different VisualTree.
You could use the Tag for binding the Command. See the following link

In Button Click Context Menu How do I bind to viewModel?

I have a button on click on that button opening a context menu, now clicking on the context menus is to be binded to viewModel. But its not happening.
<Button Content="Copy" Tag="{Binding LinkViewModel, RelativeSource={RelativeSource Mode=Self}}" Command="{Binding LinkCopyCommand, UpdateSourceTrigger=PropertyChanged}" >
<Button.ContextMenu>
<ContextMenu>
<MenuItem Header="Copy Download link " Command="{Binding Path=Parent.PlacementTarget.Tag.CopyViewCommand, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=ContextMenu}}" />
<MenuItem ... />
</ContextMenu>
</Button.ContextMenu>
</Button>
I have tried the tag property but it seems to me that its not working. The viewmodel is working fine if I bind to the button itself, but the contextMenu dataBinding is not working.
EDIT:
Now as the code is working after discussion, I think to post it here.
What the changes I made is I put UpdateSourceTrigger="Propertychanged" here is the code
<Button Content="Copy" Tag="{Binding LinkViewModel, RelativeSource={RelativeSource Mode=Self}}" Command="{Binding LinkCopyCommand, UpdateSourceTrigger=PropertyChanged}" >
<Button.ContextMenu>
<ContextMenu Width="{Binding RelativeSource={RelativeSource Self}}">
<MenuItem Header="Copy View link " Command="{Binding CopyViewCommand, UpdateSourceTrigger=PropertyChanged}" />
<MenuItem ... />
</ContextMenu>
</Button.ContextMenu>
</Button>
However I don't know how come suddenly it works, it has to work with tag property in case of Button Context menu. If anybody put some light into this I think many people like me who are new WPF and data binding will be benefited.
I'm assuming you are using this Button inside the UserControl. Please try below code
<Button Content="Copy" Tag="{Binding LinkViewModel, RelativeSource={RelativeSource Mode=Self}}" Command="{Binding LinkCopyCommand, UpdateSourceTrigger=PropertyChanged}" >
<Button.ContextMenu>
<ContextMenu>
<MenuItem Header="Copy Download link " Command="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}, Path=DataContext.CopyViewCommand}" />
<MenuItem ... />
</ContextMenu>
</Button.ContextMenu>
</Button>
<Button Content="Copy" Command="{Binding LinkCopyCommand, UpdateSourceTrigger=PropertyChanged}" >
<Button.ContextMenu>
<ContextMenu>
<MenuItem Header="Copy Download link " Command="{Binding Path=CopyViewCommand}" />
<MenuItem ... />
</ContextMenu>
</Button.ContextMenu>
CopyViewCommand Bound directly from your DataContext... which is your ViewModel..
You have to setyour ContextMenu's DataContext to your ViewModel. One way to do this is by having an Opened eventhandler for the context menu.
Take a look at my answer in the below link -
Context Menu items command binding WPF using MVVM

WPF parent child relation in binding

I am working on an application which is totally based on MVVM. I am facing a binding problem.
<ListView ItemsSource="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}},Path=DataContext.CurrentSecurityList}">
<ListView.ContextMenu>
<ContextMenu>
<MenuItem Header="Remove" Command="{Binding RelativeSource={RelativeSource FindAncestor,AncestorType={x:Type UserControl}},Path=DataContext.RemoveSecurity}"/>
</ContextMenu>
</ListView.ContextMenu>
ListView binding is working absolutely fine in this code but the problem comes when it comes to MenuItem Command Binding. Can someone explain what i am doing wrong over here.
Put a Tag in ListView to connect its ancestor to its ContextMenu:
<ListView ItemsSource="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}},Path=DataContext.CurrentSecurityList}"
Tag="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}}">
<ListView.ContextMenu>
<ContextMenu>
<MenuItem Header="Remove" Command="{Binding PlacementTarget.Tag.DataContext.RemoveSecurity, RelativeSource={RelativeSource
AncestorType=ContextMenu}}"/>
</ContextMenu>
</ListView.ContextMenu>
</ListView>
ContextMenu works on different visual tree so it is not possible to bind it like that. You need to find ContextMenu ancestor and refer to its PlacementTarget.DataContext to retrieve your command. Try something like this:
<MenuItem Header="Remove" Command="{Binding RelativeSource={RelativeSource FindAncestor,AncestorType={x:Type ContextMenu}},Path=PlacementTarget.DataContext.RemoveSecurity}"/>

Categories