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>
Related
I have a datagrid binded to observablecollection. By clicking button new row is added, and i wana to start editing new row as soon as its added. For this purpose i wana use AddingNewItem or InitializingNewItem events, because i think that i can acces newly added row in this event and call BeginInit on it (or something similar)
But in code like bellow, i cannot make events work. When i add new item, events are not fired. Am i missing something?
<DataGrid Margin="5"
AddingNewItem="enumerationsDataGrid_AddingNewItem"
AutoGenerateColumns="False"
InitializingNewItem="enumerationsDataGrid_InitializingNewItem"
ItemsSource="{Binding TypesAllAvailable}"
RowDetailsVisibilityMode="Collapsed">
<DataGrid.Columns>
<DataGridTemplateColumn>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Expander x:Name="rowDetailsExpander"
Cursor="Hand"
IsExpanded="{Binding RelativeSource={RelativeSource AncestorType={x:Type DataGridRow}, Mode=FindAncestor}, Mode=TwoWay, Path=DetailsVisibility, Converter={StaticResource visToBoolConverter}}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTextColumn Width="0.7*"
Binding="{Binding TypeName}"
Header="Name" />
<DataGridTextColumn Width="0.2*"
Binding="{Binding Enumerations.Count}"
Header="Enums"
IsReadOnly="True" />
<DataGridTextColumn Width="0.7*"
Binding="{Binding Parent.Name}"
Header="Scope"
IsReadOnly="True" />
</DataGrid.Columns>
<DataGrid.RowDetailsTemplate>
<DataTemplate>
<StackPanel>
<DataGrid x:Name="enumerationsDataGrid"
Margin="5 0 5 5"
AddingNewItem="enumerationsDataGrid_AddingNewItem"
AutoGenerateColumns="false"
ItemsSource="{Binding Enumerations}"
SizeChanged="enumerationsDataGrid_SizeChanged">
<DataGrid.RowStyle>
<Style BasedOn="{StaticResource MetroDataGridRow}"
TargetType="DataGridRow">
<Setter Property="Tag" Value="{Binding RelativeSource={RelativeSource AncestorType={x:Type DataGrid}}, Path=DataContext}" />
<Setter Property="ContextMenu">
<Setter.Value>
<ContextMenu>
<MenuItem Command="{Binding PlacementTarget.Tag.RemoveEnumerationValueCommand, RelativeSource={RelativeSource AncestorType=ContextMenu}}"
CommandParameter="{Binding}"
Header="_Delete enumeration" />
</ContextMenu>
</Setter.Value>
</Setter>
</Style>
</DataGrid.RowStyle>
<DataGrid.Columns>
<DataGridTextColumn Binding="{Binding Name}"
Header="Enumerations" />
</DataGrid.Columns>
</DataGrid>
<TextBlock Margin="5 0 5 5"
HorizontalAlignment="Center"
Foreground="Black"
Text="No enumerations found."
Visibility="{Binding Items.IsEmpty, Converter={StaticResource bool2VisibilityConverter}, ElementName=enumerationsDataGrid}" />
<Button Width="110"
Margin="5 0 0 5"
HorizontalAlignment="Left"
Command="{Binding AddEnumerationValueCommand}"
CommandParameter="{Binding}"
Content="New enumeration" />
</StackPanel>
</DataTemplate>
</DataGrid.RowDetailsTemplate>
<DataGrid.RowStyle>
<Style BasedOn="{StaticResource MetroDataGridRow}"
TargetType="DataGridRow">
<!--<Setter Property="DetailsVisibility" Value="{Binding ElementName=rowDetailsExpander}, Path=IsExpanded}" /> -->
<Setter Property="Tag" Value="{Binding RelativeSource={RelativeSource AncestorType={x:Type UserControl}}, Path=DataContext}" />
<Setter Property="ContextMenu">
<Setter.Value>
<ContextMenu>
<MenuItem Command="{Binding Parent.RemoveStateTypeCommand}"
CommandParameter="{Binding}"
Header="_Delete state" />
<!-- Command="{Binding PlacementTarget.Tag.DeleteObjectiveStateCommand, RelativeSource={RelativeSource AncestorType=ContextMenu}}" -->
</ContextMenu>
</Setter.Value>
</Setter>
</Style>
</DataGrid.RowStyle>
</DataGrid>
Events are in same UserControl and breakpoint not hitting...
private void enumerationsDataGrid_AddingNewItem(object sender, AddingNewItemEventArgs e)
{
}
private void enumerationsDataGrid_InitializingNewItem(object sender, InitializingNewItemEventArgs e)
{
}
EDIT
Im using DataGrid component which should have event AddingNewItem and InitializingNewItem
https://learn.microsoft.com/en-us/dotnet/api/system.windows.controls.datagrid.addingnewitem?view=netframework-4.7.2
https://learn.microsoft.com/en-us/dotnet/api/system.windows.controls.datagrid.initializingnewitem?view=netframework-4.7.2
When i initialize datagrid as in example above, bind it on Observable collection and add item to bound collection, none of this events are fired. And im curious why those events are not fired?
Binding seems okay, because item is correctly added into datagrid.
EDIT2
I know about notify events and observable collections, but watching this i can access only ViewModel class. But what i wana is, that when user add new row, that row will switch into Edit mode and user can write into its cells. Instead of double clicking on it.
So thats why im trying to use those two events. BEcause when i have reference on cell, i can call BeginEdit https://learn.microsoft.com/en-us/dotnet/api/system.windows.controls.datagrid.beginedit?view=netframework-4.7.2
I've setup a ListView with grouping and I would like to retrieve the GroupName when I right click on the group in MVVM. I've placed a ContextMenu on my group style, and I was trying to use the EventToCommand from System.Windows.Interactivity to get the underlying item.
Here the relevant part:
<ListView.GroupStyle>
<GroupStyle>
<GroupStyle.ContainerStyle>
<Style TargetType="{x:Type GroupItem}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Expander IsExpanded="False" Template="{StaticResource CustomizedExpander}" Background="#FFEBEBEB" BorderThickness="0" ContextMenu="{StaticResource GroupContextMenu}">
<i:Interaction.Triggers>
<i:EventTrigger EventName="MouseRightButtonDown">
<i:InvokeCommandAction Command="{Binding Path=OnCategorySelected}" CommandParameter="{Binding Name}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
<Expander.Header>
<StackPanel Orientation="Horizontal">
<!--N.B. The "Name" property is part of the CollectionViewSource-->
<TextBlock Text="{Binding Name}" FontWeight="Bold" Foreground="#FF767676" VerticalAlignment="Bottom" />
<TextBlock Text="{Binding ItemCount}" Foreground="#FF454545" FontWeight="Bold" FontStyle="Italic" Margin="10,0,0,0" VerticalAlignment="Bottom" />
<TextBlock Text=" item(s)" Foreground="#FF767676" FontStyle="Italic" VerticalAlignment="Bottom" />
</StackPanel>
</Expander.Header>
<ItemsPresenter />
</Expander>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</GroupStyle.ContainerStyle>
</GroupStyle>
</ListView.GroupStyle>
I don't know if it's the right way to do it but it seems the command is not triggered at all.
Any suggestion?
UPDATE:
The whole thing was much simpler than I thought. The interaction part was not required at all. Fixing the binding is enough to get the underlying category when the context menu is shown:
<ListView ItemsSource="{Binding Modifications}" SelectedItem="{Binding SelectedItem}">
<ListView.Resources>
<ContextMenu x:Key="ItemContextMenu">
<MenuItem Header="Execute" Command="{Binding Path=DataContext.OnExecuteScript, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=ListView}}" Background="WhiteSmoke" Visibility="{Binding CanExecute, Converter={StaticResource BooleanToVisibilityConverter}}" />
<MenuItem Header="Execute up to this" Command="{Binding Path=DataContext.OnExecuteScriptUpToThis, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=ListView}}" Background="WhiteSmoke" Visibility="{Binding CanOnlyBeExecutedSequentially, Converter={StaticResource BooleanToVisibilityConverter}}" />
<MenuItem Header="Drop" Command="{Binding Path=DataContext.OnExecuteDrop, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=ListView}}" Visibility="{Binding Drop, Converter={StaticResource BooleanToVisibilityConverter}}" Background="WhiteSmoke" />
<MenuItem Header="Dump" Command="{Binding Path=DataContext.OnExecuteDump, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=ListView}}" Visibility="{Binding CanDump, Converter={StaticResource BooleanToVisibilityConverter}}" Background="WhiteSmoke" />
</ContextMenu>
<ContextMenu x:Key="GroupContextMenu">
<MenuItem Header="Dump all" Command="{Binding Path=DataContext.OnExecuteDumpAll, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=ListView}}" CommandParameter="{Binding Name}" Background="WhiteSmoke" />
</ContextMenu>
</ListView.Resources>
<ListView.View>
<GridView>
<GridViewColumn Header="Type" Width="120" DisplayMemberBinding="{Binding Type}" />
<GridViewColumn Header="Object Name" Width="Auto" DisplayMemberBinding="{Binding DisplayName}" />
<GridViewColumn Header="" Width="50">
<GridViewColumn.CellTemplate>
<DataTemplate>
<CheckBox IsChecked="{Binding Deploy}" IsHitTestVisible="False" />
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn Header="Object Name" Width="300" DisplayMemberBinding="{Binding ObjectName}" />
</GridView>
</ListView.View>
<ListView.ItemContainerStyle>
<Style TargetType="{x:Type ListViewItem}" BasedOn="{StaticResource MetroListViewItem}" >
<Setter Property="ContextMenu" Value="{StaticResource ItemContextMenu}" />
<Style.Triggers>
<DataTrigger Binding="{Binding Drop}" Value="True">
<Setter Property="Foreground" Value="Red" />
</DataTrigger>
</Style.Triggers>
</Style>
</ListView.ItemContainerStyle>
<ListView.GroupStyle>
<GroupStyle>
<GroupStyle.ContainerStyle>
<Style TargetType="{x:Type GroupItem}">
<Setter Property="ContextMenu" Value="{StaticResource GroupContextMenu}" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Expander IsExpanded="False" Template="{StaticResource CustomizedExpander}" Background="#FFEBEBEB" BorderThickness="0">
<Expander.Header>
<StackPanel Orientation="Horizontal">
<!--N.B. The "Name" property is part of the CollectionViewSource-->
<TextBlock Text="{Binding Name}" FontWeight="Bold" Foreground="#FF767676" VerticalAlignment="Bottom" />
<TextBlock Text="{Binding ItemCount}" Foreground="#FF454545" FontWeight="Bold" FontStyle="Italic" Margin="10,0,0,0" VerticalAlignment="Bottom" />
<TextBlock Text=" item(s)" Foreground="#FF767676" FontStyle="Italic" VerticalAlignment="Bottom" />
</StackPanel>
</Expander.Header>
<ItemsPresenter />
</Expander>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</GroupStyle.ContainerStyle>
</GroupStyle>
</ListView.GroupStyle>
</ListView>
First of all, i think i've figured out why your Command isnt firing.
Since you are in an Template, the DataContext has Changed. Therefore your CommandBinding should look like this:
<i:InvokeCommandAction Command="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ListView}}, Path=DataContext.OnCategorySelected}" CommandParameter="{Binding}"/>
Also, your CommandParameter should not be Name because you'll only get a string in the end instead the whole Object.
If you use my above code you will get the whole CollectionViewGroup instead. In said CollectionViewGroup you'll find all the Items in the group. If you are fine with getting just the Groupname you can proceed with you implementation of course.
I dont really understand why you are using a ContextMenu and what it does (Since you dont posted that code), but if you are interested in how you can display the grouped Items in such a ContextMenu, you can do it like this:
<Expander IsExpanded="False" Background="#FFEBEBEB" BorderThickness="0" >
<Expander.ContextMenu>
<ContextMenu ItemsSource="{Binding Items}"/>
</Expander.ContextMenu>
</Expander>
Since we now know, what we have to deal with (It's still an CollectionViewGroup) we can set the Items of it as ItemsSource of the ContextMenu.
Hope this helps!
I am working with a Wpf/XAML based application which has DataGrid with one of the DataGridColumn containing the TreeView control to let user select the item required.
<DataGrid.Columns>
<DataGridTextColumn Header="SerialNumber" Width="*" Binding="{Binding SerialNumber}" />
<DataGridTemplateColumn Header="Field" Width="*">
<DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<uControls:FieldTreeViewControl />
</DataTemplate>
</DataGridTemplateColumn.CellEditingTemplate>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding CurrentField.FieldName,Mode=TwoWay}"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
Field column in above code is the one which displays treeviewcontrol in the DataGrid cell by referencing the FieldTreeViewControl and it works perfectly. The xaml code of FieldTreeViewControl is :
<UserControl>
.......
<Grid>
<TreeView x:Name="MyUITreeView" ItemsSource="{Binding Fields}">
<TreeView.Resources>
<Style TargetType="{x:Type TreeViewItem}">
<Setter Property="IsExpanded" Value="{Binding IsNodeExpanded, Mode=TwoWay}" />
<Setter Property="IsSelected" Value="{Binding IsNodeSelected, Mode=TwoWay}" />
</Style>
<DataTemplate x:Key="NormalTemplate">
<StackPanel Orientation="Horizontal">
<!--<TextBlock Text="-" Margin="3"/>-->
<TextBlock Text="{Binding FieldName}" Margin="3"/>
<StackPanel.ContextMenu>
<ContextMenu Name="myChildMenu" DataContext="{Binding PlacementTarget,RelativeSource={RelativeSource Self}}">
<MenuItem Header="Add Field" Command="{Binding DataContext.AddFieldCommand}" CommandParameter="{Binding}">
</MenuItem>
</ContextMenu>
</StackPanel.ContextMenu>
</StackPanel>
</DataTemplate>
<!--<DataTemplate x:Key="EditTemplate">
<TextBox Text="{Binding FieldName}"/>
</DataTemplate>-->
</TreeView.Resources>
<TreeView.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding Children}">
<ContentPresenter Content="{Binding}">
<ContentPresenter.Style>
<Style TargetType="{x:Type ContentPresenter}">
<Setter Property="ContentTemplate" Value="{StaticResource NormalTemplate}"/>
<Style.Triggers>
<DataTrigger
Binding="{Binding IsNodeSelected,
RelativeSource={RelativeSource
FindAncestor,
AncestorType={x:Type TreeViewItem}}}"
Value="True">
</DataTrigger>
</Style.Triggers>
</Style>
</ContentPresenter.Style>
</ContentPresenter>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
</Grid>
Now when user completes selecting an item from treeview I want to collapse treeview to show only that selected item. Later when user wants to change his selection , then the treeview should be available again by clicking on that column/cell. Is that possible to do this in wpf/XAML ? I tried to follow the link but not able to implement it for my scenario.
In my xaml form i have a dropdown button the dropdown values will bind from database and my requirement is i want to add one image for each dropdown value in the dropdown box, how can get this.....
<ctrl:DropDownButton Grid.Column="1"
Height="25"
Text="Add Question"
Width="125"
Margin="5,0,10,0">
<ctrl:DropDownButton.DropDownContextMenu>
<ContextMenu ItemsSource="{Binding Source={StaticResource QuestionTypes}}">
<ContextMenu.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Background="WhiteSmoke"/>
</ItemsPanelTemplate>
</ContextMenu.ItemsPanel>
<ContextMenu.ItemContainerStyle>
<Style TargetType="MenuItem">
<Setter Property="Command" Value="{Binding DataContext.AddQuestionCommand,
RelativeSource={RelativeSource AncestorType=UserControl}}" />
<Setter Property="CommandParameter" Value="{Binding}" />
</Style>
</ContextMenu.ItemContainerStyle>
<ContextMenu.ItemTemplate>
<DataTemplate>
<Grid Margin="-20,0,-50,0">
<TextBlock Grid.Column="1" Text="{Binding Converter={StaticResource EnumDescriptionConverter}}"/>
</Grid>
</DataTemplate>
</ContextMenu.ItemTemplate>
</ContextMenu>
</ctrl:DropDownButton.DropDownContextMenu>
</ctrl:DropDownButton>
*I wrote this outside of VisualStudio so please check the syntax
you should add an Image element to your DataTempalte and using a converter for the enum value resolve the path to the image you would like to present.
<ContextMenu.ItemTemplate>
<DataTemplate>
<Grid Margin="-20,0,-50,0">
<Grid>
<Grid.ColumnDefenitions>
<ColumnDefenition />
<ColumnDefenition />
</Grid.ColumnDefenitions>
<Image Source="{Binding Path=., Converter={StaticResource EnumImageConverter}}" />
<TextBlock Grid.Column="1" Text="{Binding Path=., Converter={StaticResource EnumDescriptionConverter}}"/>
</Grid>
</DataTemplate>
</ContextMenu.ItemTemplate>
i want for my Listbox in WPF a contextmenu.
I did it with a contextmenu for the whole listbox, but you can richt-click to get the contextmenu even if you don't click on a item.
I found something at google, but this didn't work out.
I tried something like this:
<ListBox Margin="5" ItemsSource="{Binding}">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}">
<TextBlock.ContextMenu>
<ContextMenu>
<MenuItem Header="{Binding Name}" Click="MenuItemName_Click"/>
<MenuItem Header="{Binding Capital}" Click="MenuItemCapital_Click"/>
<MenuItem Header="{Binding Population}" Click="MenuItemPopulation_Click"/>
</ContextMenu>
</TextBlock.ContextMenu>
</TextBlock>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
I tried it with a textblock like in the example, with other elements like in other tutorials, i tired it without and many other things- but nothing worked. No contextmenu for my listbox items :(
later i tried something like this:
<ListBox.ItemTemplate>
<DataTemplate>
<ListBoxItem>
<ListBoxItem.ContextMenu>
<ContextMenu>
<MenuItem/>
</ContextMenu>
</ListBoxItem.ContextMenu>
</ListBoxItem>
</DataTemplate>
</ListBox.ItemTemplate>
But it didn't work too.
Can someone give me a hint/working example :)?
thank you
I would set the ContextMenu in the ListBoxItem's style, rather than in the DataTemplate:
<ListBox Name="simpleListBox"
ItemsSource="{Binding SimpleList}"
DisplayMemberPath="Name">
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Setter Property="ContextMenu">
<Setter.Value>
<ContextMenu>
...
</ContextMenu>
</Setter.Value>
</Setter>
</Style>
</ListBox.ItemContainerStyle>
</ListBox>