Bind Command to MenuItem - c#

I have ListView and i am trying to bind command to ContextMenu of ListView.
<ListView x:Name="listView1" ItemsSource="{Binding Path=Persons}">
<ListView.Resources>
<ContextMenu x:Key="ItemContextMenu">
<MenuItem Header="Add" />
<MenuItem Header="Edit"/>
<Separator/>
<MenuItem Header="Delete" Command="{Binding Msg}" />
</ContextMenu>
</ListView.Resources>
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<!--<EventSetter Event="PreviewMouseLeftButtonDown" />--><!--Handler="OnListViewItem_PreviewMouseLeftButtonDown" />-->
<Setter Property="ContextMenu" Value="{StaticResource ItemContextMenu}"/>
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
</Style>
</ListView.ItemContainerStyle>
<ListView.View>
<GridView>
<GridViewColumn Header="Name" DisplayMemberBinding="{Binding Path=Name}" />
<GridViewColumn Header="Sur Name" DisplayMemberBinding="{Binding Path=SurName}" />
<GridViewColumn Header="Age" DisplayMemberBinding="{Binding Path=Age}" />
</GridView>
</ListView.View>
</ListView>
<Button Content="Message" Command="{Binding Msg}" />
Binding to Button works well but when i click to delete item in ContextMenu, command is not working! Why?

Your problem is related to using bindings in resources. They normally don't work unless you are using something like {Binding Path=Value,Source={x:Static Some.StaticProperty}}. In order for ElementName or DataContext bindings to work you need to resort to help of ElementSpy and DataContextSpy. In your particular case if you are relying on DataContext binding, your XAML should look like this:
<ListView.Resources>
<DataContextSpy x:Name="spy" />
<ContextMenu x:Key="ItemContextMenu">
<MenuItem Header="Add" />
<MenuItem Header="Edit"/>
<Separator/>
<MenuItem Header="Delete" Command="{Binding DataContext.Msg,Source={StaticResource spy}}" />
</ContextMenu>
</ListView.Resources>

Related

Show ContextMenu only when Right clicked mouse is over on a specific datagridrow WPF C#

I have a Page with a datagrid --> xaml code below:
<DataGrid x:Name="DataGrid"
IsReadOnly="True"
AutoGenerateColumns="False"
SelectionUnit="FullRow"
BorderBrush="{x:Null}"
HorizontalContentAlignment="Stretch"
ColumnWidth="Auto" Focusable="False"
MouseDoubleClick="DataGridArticoli_MouseDoubleClick"
GridLinesVisibility="None"
HeadersVisibility="Column"
UseLayoutRounding="False"
SelectionChanged="DataGrid_SelectionChanged">
<!--Columns-->
<DataGrid.Columns>
<DataGridTextColumn col_1/>
<DataGridTextColumn col_2/>
<DataGridTextColumn col_3/>
<DataGridTextColumn col_4/>
<DataGridTextColumn col_5/>
<DataGridTextColumn col_6/>
<DataGridTextColumn col_7/>
<DataGridTextColumn col_8/>
<DataGridTextColumn col_9/>
<DataGridTextColumn col_10/>
</DataGrid.Columns>
<DataGrid.ContextMenu>
<ContextMenu x:Name="ContextMenu">
<MenuItem Header="Remove" Click="Remove_ContextMenuClick">
<MenuItem.Icon>
<Image Source="/resources/delete.png"/>
</MenuItem.Icon>
</MenuItem>
<MenuItem x:Name="btnOpen" Header="Open" Click="Open_ContextMenuClick" IsEnabled="False">
<MenuItem.Icon>
<Image Source="/resources/open.png"/>
</MenuItem.Icon>
</MenuItem>
</ContextMenu>
</DataGrid.ContextMenu>
</DataGrid>
In this case context menu appear in each position of the mouse (when right clicked). I want show the context menu only in the selected row and only when mouse pointer is over the row selected. I tried different solutions, but I didn't find the right one. Someone can help me with some example code?
You could use a RowStyle with a Trigger:
<DataGrid x:Name="DataGrid" ...>
<DataGrid.Resources>
<ContextMenu x:Key="ContextMenu">
<MenuItem Header="Remove" Click="Remove_ContextMenuClick">
<MenuItem.Icon>
<Image Source="/resources/delete.png"/>
</MenuItem.Icon>
</MenuItem>
<MenuItem x:Name="btnOpen" Header="Open" Click="Open_ContextMenuClick" IsEnabled="False">
<MenuItem.Icon>
<Image Source="/resources/open.png"/>
</MenuItem.Icon>
</MenuItem>
</ContextMenu>
</DataGrid.Resources>
<DataGrid.Columns>
...
</DataGrid.Columns>
<DataGrid.RowStyle>
<Style TargetType="DataGridRow">
<Style.Triggers>
<Trigger Property="IsSelected" Value="True">
<Setter Property="ContextMenu" Value="{StaticResource ContextMenu}" />
</Trigger>
</Style.Triggers>
</Style>
</DataGrid.RowStyle>
</DataGrid>

WPF : Remove context menu items based on condition

I have a context menu in WPF with following items:
<ContextMenu x:Key="MyContextMenu">
<MenuItem Header="{x:Static localization:Resources.MyContext_Command1}" Command="{Binding Command1}" />
<MenuItem Header="{x:Static localization:Resources.MyContext_Command2}" Command="{Binding Command2}" />
<Separator />
<MenuItem Header="{x:Static localization:Resources.MyContext_Command3}" Command="{Binding Command3}" Visibility="{Binding IsItemActive, Converter={converters:BooleanToVisibilityConverter}}" />
<MenuItem Header="{x:Static localization:Resources.MyContext_Command4}" Command="{Binding Command4}" Visibility="{Binding IsItemActive, Converter={converters:BooleanToVisibilityConverter}}" />
<Separator Visibility="{Binding IsItemActive, Converter={converters:BooleanToVisibilityConverter}}"/>
</ContextMenu>
With the above code these menu items(Command3 and Command4) are appearing grey(Disabled) when IsItemActive = false and appear black(Enabled) when IsItemActive = true. But i want my Menuitems(Command3 and Command4) and also Seperator to Disappear / Appear from context menu based on "IsItemActive".How can i achieve this ?
This should do the trick. But I cannot get my bindings to work now so it's not tested. You will have to give a name to the element that you are applying this in order to make the binding work. For my case, it's a window.
Create a dependency property for IsItemActive only then you can bind.
Tip : type propdp and double tab in Visual Studio to use code snippet.
<Window x:Class.....
x:Name="mainwindow"
...>
Then in the <Window.Resources> define the styles
<Style x:Key="MenuItemStyle" TargetType="MenuItem">
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=mainwindow,Path=IsItemActive}" Value="False">
<Setter Property="Visibility" Value="Collapsed" />
</DataTrigger>
<DataTrigger Binding="{Binding ElementName=mainwindow,Path=IsItemActive}" Value="True">
<Setter Property="Visibility" Value="Visible" />
</DataTrigger>
</Style.Triggers>
</Style>
Duplicate the style and change TargetType="Separator" Then apply the style on your context menu items and separator.
<ContextMenu x:Key="MyContextMenu">
<MenuItem Header="{x:Static localization:Resources.MyContext_Command1}" Command="{Binding Command1}" />
<MenuItem Header="{x:Static localization:Resources.MyContext_Command2}" Command="{Binding Command2}" />
<Separator />
<MenuItem Header="{x:Static localization:Resources.MyContext_Command3}" Command="{Binding Command3}" Style="{StaticResource MenuItemStyle}" />
<MenuItem Header="{x:Static localization:Resources.MyContext_Command4}" Command="{Binding Command4}" Style="{StaticResource MenuItemStyle}" />
<Separator Style="{StaticResource SeparatorStyle}"/>
</ContextMenu>

C# WPF Extending ListView row

I have ListView and I want the last item in the row displayed below the actual row items. Raw copy of xaml:
<ListView x:Name="ListViewRotations" FontFamily="Consolas" MouseDoubleClick="ListViewRotations_MouseDoubleClick">
<ListView.ContextMenu>
<ContextMenu>
<MenuItem Header="Remove" Click="ContextMenuItemRemoveClicked" />
</ContextMenu>
</ListView.ContextMenu>
<ListView.View>
<GridView>
<GridViewColumn Width="120" Header="Max Craftsmanship" DisplayMemberBinding="{Binding RotationInfo.MaxCraftsmanship}" />
<GridViewColumn Width="120" Header="Min Craftsmanship" DisplayMemberBinding="{Binding RotationInfo.MinCraftsmanship}" />
<GridViewColumn Width="80" Header="Min Control" DisplayMemberBinding="{Binding RotationInfo.MinControl}" />
<GridViewColumn Width="50" Header="CP" DisplayMemberBinding="{Binding RotationInfo.CP}" />
<GridViewColumn Width="130" Header="Score" DisplayMemberBinding="{Binding RotationInfo.Score}" />
<GridViewColumn Width ="Auto" Header="Rotation Time" DisplayMemberBinding="{Binding RotationInfo.RotationTime}" />
<GridViewColumn Width ="Auto" Header="Rotation" >
<GridViewColumn.CellTemplate>
<DataTemplate>
<ListBox ItemsSource="{Binding Images}">
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal" />
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="Margin" Value="0"/>
<Setter Property="Padding" Value="0"/>
</Style>
</ListBox.ItemContainerStyle>
<ListBox.ItemTemplate>
<DataTemplate DataType="CraftingActionContainer">
<Grid Height="25">
<Image Width="25" Height="25" Source ="{Binding BitmapSource}" VerticalAlignment="Top" />
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
</GridView>
</ListView.View>
</ListView>
As you can see, last item is actually a ListBox and since it is quite long I want it to appear on new line.
So far I only thought of creating 2 types of items, one with text data and the other for holding the last item and using template selector. Maybe you know of other ways. Thanks in advance.
Edit:
Images included.
Current result
Wanted result
Generally speaking, is it possible to separate last item from row and modify it so it appears below other items
You can use DataGrid with row details;
<Grid Margin="10">
<DataGrid Name="dgUsers" AutoGenerateColumns="False">
<DataGrid.Columns>
<DataGridTextColumn Header="Name" Binding="{Binding Name}" />
<DataGridTextColumn Header="Birthday" Binding="{Binding Birthday}" />
</DataGrid.Columns>
<DataGrid.RowDetailsTemplate>
<DataTemplate>
<TextBlock Text="{Binding Details}" Margin="10" />
</DataTemplate>
</DataGrid.RowDetailsTemplate>
</DataGrid>
</Grid>
Full tutorial you can find here: Row Details

C# WPF ContextMenu: MenuItem does not react to click

I have a C# WPF-Application. In the XAML i have a Datagrid, to which I have added a ContextMenu, that looks like this:
<DataGrid.ContextMenu>
<ContextMenu ItemsSource="{Binding Categories}">
<ContextMenu.ItemTemplate>
<DataTemplate>
<MenuItem Header="{Binding Name}" Background="{Binding Brush}" Click="MenuItem_Click" Tag="{Binding Id}" />
</DataTemplate>
</ContextMenu.ItemTemplate>
</ContextMenu>
</DataGrid.ContextMenu>
When using the application it looks like this: http://imgur.com/3UTj1Xd
The problem is, that when clicking the color part of the box (which I'm guessing is part of some internal grid) the MenuItem_Click event is fired. However, when I'm clicking on the grey part of the MenuItem the click event is not fired. Does anyone know why it behaves this way? And is there a way to fix this?
Additionally, it would be great to be able to color the whole menu item, not just the small box inside. Is there a way to do this?
You could try to decrease the Padding of the ContextMenu and the BorderThickess of the MenuItems:
<DataGrid.ContextMenu>
<ContextMenu ItemsSource="{Binding Categories}" Padding="0">
<ContextMenu.ItemTemplate>
<DataTemplate>
<MenuItem Header="{Binding Name}" Background="{Binding Brush}" Click="MenuItem_Click" Tag="{Binding Id}"
BorderThickness="0"/>
</DataTemplate>
</ContextMenu.ItemTemplate>
</ContextMenu>
</DataGrid.ContextMenu>
Or
<DataGrid.ContextMenu>
<ContextMenu ItemsSource="{Binding Categories}" Padding="0">
<ContextMenu.ItemContainerStyle>
<Style TargetType="MenuItem">
<Setter Property="Header" Value="{Binding Name}" />
<Setter Property="Tag" Value="{Binding Id}" />
<Setter Property="Background" Value="{Binding Brush}" />
<EventSetter Event="Click" Handler="MenuItem_Click" />
</Style>
</ContextMenu.ItemContainerStyle>
</ContextMenu>
</DataGrid.ContextMenu>

WPF Delete button in listview mvvm

I have a button inside a listview to delete selected item.when i click on the button RemoveSubjectCommand is not firing. if i put the button outside the listview it is working fine. hopw this is just because of nested item. how can i solve this problem?
<ListView HorizontalAlignment="Stretch" VerticalAlignment="Stretch"
HorizontalContentAlignment="Stretch" Grid.ColumnSpan="3" Grid.Row="2"
ItemsSource="{Binding AssignedSubjects}">
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Setter Property="HorizontalContentAlignment" Value="Center" />
</Style>
</ListView.ItemContainerStyle>
<ListView.View>
<GridView>
<GridViewColumn Width="140" Header="Subjects" DisplayMemberBinding="{Binding Name}" />
<GridViewColumn Width="auto">
<GridViewColumn.CellTemplate>
<DataTemplate>
<Button Content="X" Command="{Binding RemoveSubjectCommand}" />
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
</GridView>
</ListView.View>
</ListView>
View Model,
private ICommand removeSubjectCommand;
**
public ICommand RemoveSubjectCommand
{
get { return removeSubjectCommand ?? (removeSubjectCommand = new RelayCommand(param => this.RemoveSubject(), null)); }
}
**
private void RemoveSubject()
{ ***
}
If i put following code, it will work fine.
<ListView.InputBindings>
<KeyBinding Key="Delete" Command="{Binding RemoveSubjectCommand}" />
</ListView.InputBindings>
That is because DataContext of button is ListBoxItem DataContext. So you need to go to parent ListView DataContext.
one way to do that, is to give ListView a name, and to bind with element name
<ListView Name="lv" HorizontalAlignment="Stretch" VerticalAlignment="Stretch"
HorizontalContentAlignment="Stretch" Grid.ColumnSpan="3" Grid.Row="2"
ItemsSource="{Binding AssignedSubjects}">
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Setter Property="HorizontalContentAlignment" Value="Center" />
</Style>
</ListView.ItemContainerStyle>
<ListView.View>
<GridView>
<GridViewColumn Width="140" Header="Subjects" DisplayMemberBinding="{Binding Name}" />
<GridViewColumn Width="auto">
<GridViewColumn.CellTemplate>
<DataTemplate>
<Button Content="X" Command="{Binding ElementName=lv,Path=DataContext.RemoveSubjectCommand}" />
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
</GridView>
</ListView.View>
</ListView>
You need to bind the Command to using FindAncestor. Otherwise, it'll try to bind to a RemoveSubjectCommand using the ListViewItem's datacontext, not the ViewModel's.
Try this:
<Button Content="X" Command="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=ListView}, Path=DataContext.RemoveSubjectCommand}" />
if i put the button outside the listview it is working fine
Because RemoveSubjectCommand property is defined in the data context of ListView, not in the data context of item.

Categories