Bind ContextMenu Item inside Button to ViewModel - c#

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>

Related

Execute the Command in Listview MenuItem MVVM

Why does the Command in my Listview Menu Item is not executing?
This is the code on my Listview
<ListView ItemsSource="{Binding ListDataCorrection}" >
<ListView.View>
<GridView>
<GridViewColumn Header="Validate">
<GridViewColumn.CellTemplate>
<DataTemplate>
<Button Content="Update" Margin="5" Cursor="Hand" Command="{Binding RelativeSource={RelativeSource FindAncestor,
AncestorType={x:Type ListView}}, Path=DataContext.ValidateCommand}"/>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
</GridView>
</ListView.View>
<ListView.ContextMenu>
<ContextMenu>
<MenuItem Header="Remove" Command="{Binding RelativeSource={RelativeSource FindAncestor,
AncestorType={x:Type ListView}}, Path=DataContext.ValidateAllCommand}">
</MenuItem>
</ContextMenu>
</ListView.ContextMenu>
</ListView>
But the weird thing is the ValidateCommand inside the Gridview is executed.
While the Command in the MenuItem is not.
What's is wrong with my Binding?
And i also checked if the Command name is correct. If not i should receive an error saying that the command is not found in the ViewModel
Thank you.
I'm having this problem too sometimes with MenuItems inside a ContextMenu. I guess that the ContextMenu can't find the correct DataContext.
My soltuion for this is a BindingProxy-class which looks like:
public class BindingProxy : Freezable
{
public static readonly DependencyProperty DataProperty = DependencyProperty.Register(
"Data", typeof (object), typeof (BindingProxy), new UIPropertyMetadata(null));
/// <summary>
/// This Property holds the DataContext
/// </summary>
public object Data
{
get { return GetValue(DataProperty); }
set { SetValue(DataProperty, value); }
}
protected override Freezable CreateInstanceCore()
{
return new BindingProxy();
}
}
In the resources of your view (UserControl or Window) you have to add the proxy like:
<codeBase:BindingProxy x:Key="proxy" Data="{Binding}"/>
And in your MenuItem you can use it with:
<MenuItem Header="Remove" Command="{Binding Source={StaticResource proxy}, Path=Data.ValidateAllCommand}"/>
A Menu (as well as a Popup for example) is not part of the visual-tree, as it is created on demand. Since its is not part of the visual-tree, it will not inherit its parents DataContext. However you can still bind to your ListView using the PlacementTarget-property in your binding:
<ListView.ContextMenu>
<ContextMenu>
<MenuItem Header="Remove"
Command="{Binding Path=PlacementTarget.DataContext.ValidateAllCommand}">
</MenuItem>
</ContextMenu>
</ListView.ContextMenu>
Why does the Command in my Listview Menu Item is not executing?
Because the ListView is not a visual ancestor of the MenuItem so the RelativeSource of the binding isn't found.
If you change the AncestorType to ContextMenu and bind to its PlacementTarget, it should work:
<ContextMenu>
<MenuItem Header="Remove" Command="{Binding RelativeSource={RelativeSource AncestorType={x:Type ContextMenu}},
Path=PlacementTarget.DataContext.ValidateAllCommand}">
</MenuItem>
</ContextMenu>

Bind ContextMenuItem Property to a DataGrid or an other Element Property

I trying to bind ContextMenuItem property to a DataGrid Property as flow:
<DataGrid Name="DG_Data" >
<DataGrid.Columns>
<DataGridTextColumn Header="COL1"/>
<DataGridTextColumn Header="COL2"/>
<DataGridTextColumn Header="COL3"/>
<DataGridTextColumn Header="COL4"/>
</DataGrid.Columns>
</DataGrid>
<Button Name="BTN_OpenContext" Content="CLICK TO OPEN">
<Button.ContextMenu>
<ContextMenu Name="CM_ContextMenu">
<MenuItem Header="{Binding ElementName=DG_Data,Path=Columns.Count,FallbackValue=BindingFailed}" />
</ContextMenu>
</Button.ContextMenu>
</Button>
I have tried also using the different way with RelativeSource
<MenuItem Header="{Binding RelativeSource={RelativeSource Mode=FindAncestor,AncestorLevel=2,AncestorType=FrameworkElement}, FallbackValue=BindingFailed}" />
I have just be able to bind to the parent (the ContextMenu).
Thanks
The ContextMenu can't bind to the DataGrid using ElementName, but it can't bind to a property of the parent ContextMenu's PlacementTarget (which is the Button).
So you could bind the Button's Tag property to the DataGrid, and then bind to the Tag property from the MenuItem:
<Button Name="BTN_OpenContext" Content="CLICK TO OPEN"
Tag="{Binding ElementName=DG_Data}">
<Button.ContextMenu>
<ContextMenu Name="CM_ContextMenu">
<MenuItem Header="{Binding Path=PlacementTarget.Tag.Columns.Count,
RelativeSource={RelativeSource AncestorType=ContextMenu}, FallbackValue=BindingFailed}" />
</ContextMenu>
</Button.ContextMenu>
</Button>

Hide specific context menu in datagrid upon selected row wpf mvvm

I got one issue on datagrid using WPF Mvvm. I set context menu on datagrid.
here is my code.
<DataGrid.ContextMenu>
<ContextMenu IsEnabled="{Binding IsEnableCaseRefNo}"
DataContext="{Binding PlacementTarget.DataContext, RelativeSource={RelativeSource Self}}" >
<MenuItem Header=" - View Case" >
<i:Interaction.Triggers>
<i:EventTrigger EventName="Click">
<i:InvokeCommandAction Command="{Binding ContextCommand}" CommandParameter="VCD"></i:InvokeCommandAction>
</i:EventTrigger>
</i:Interaction.Triggers>
</MenuItem>
<MenuItem Header=" - Cheque" Visibility="{Binding SyncColumnVisibility, Converter={StaticResource visibilityConverter}}">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Click">
<i:InvokeCommandAction Command="{Binding ContextCommand}" CommandParameter="BMK"></i:InvokeCommandAction>
</i:EventTrigger>
</i:Interaction.Triggers>
</MenuItem>
<MenuItem Header=" - Cash" Visibility="{Binding SyncColumnVisibility, Converter={StaticResource visibilityConverter}}">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Click">
<i:InvokeCommandAction Command="{Binding ContextCommand}" CommandParameter="UNBMK"></i:InvokeCommandAction>
</i:EventTrigger>
</i:Interaction.Triggers>
</MenuItem>
</ContextMenu>
</DataGrid.ContextMenu>
I can show and hide context menu using visibility property. but now i want to enable/disable "-Cheque" context menu upon user selection. how can I disable 'Cheque' context menu when there are 100 over dollars in donate columns(that already shown in datagrid).
Here is my datagrid :
<DataGrid Name="dgv" Background="WhiteSmoke" AutoGenerateColumns="False" CanUserAddRows="False" SelectedItem="{Binding SelectedItems,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"
CanUserDeleteRows="False" Grid.Row="2" ItemsSource="{Binding LstcaseHearingModel,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"
Grid.Column="2" HorizontalAlignment="Stretch" >
Thanks for any help.
frog
Use SelectedCellsChanged event to get desired item.
private void Dgrd_SelectedCellsChanged(object sender, SelectedCellsChangedEventArgs e)
{
SomeEntity item = (SomeEntity) Dgrd.CurrentItem;
if(item.Donate > 100)
viewModel.SyncColumnVisibility = Visibility.Collapsed;
else
viewModel.SyncColumnVisibility = Visibility.Visible;
}
As you are binding SelectedItem property to SelectedItems property of your ViewModel. So you can check the condition in its setter.

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