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>
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 have a DataGrid with a ContextMenu, I'm trying to figure out how to properly bind the context.
So far I have read that the context menu its ouside the visual tree, and therefore the DataContext is different. With that in mind the provided solution is to use the property Tag, but I still cannot make it work:
<UserControl>
<!--#region DataGrid-->
<DataGrid ItemsSource="{Binding Model.Collection}"
Tag="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=UserControl}, Path=DataContext}">
<!--#region Resources-->
<DataGrid.Resources>
<!--#region DataGridCell-->
<Style TargetType="DataGridCell">
<Setter Property="ContextMenu">
<Setter.Value>
<ContextMenu DataContext="{Binding PlacementTarget.Tag, RelativeSource={RelativeSource Self}}">
<MenuItem Header="Open Details"
Command="{Binding DataContext.OpenRowDetailsCommand, RelativeSource={RelativeSource AncestorType=ContextMenu}}"
CommandParameter="{Binding DataContext.SelectedIndex, RelativeSource={RelativeSource AncestorType=ContextMenu}}"/>
</ContextMenu>
</Setter.Value>
</Setter>
</Style>
<!--#endregion DataGridCell-->
</DataGrid.Resources>
<!--#endregion Resources-->
<!--#region DataGridColumns-->
<DataGrid.Columns>
<DataGridTextColumn Header="Filename"
Binding="{Binding FileInfo.Name}"
Width="Auto" />
</DataGrid.Columns>
<!--#endregion DataGridColumns-->
</DataGrid>
<!--#endregion DataGrid-->
The DataContext of the UserControl is working fine as I have other commands which uses the DataContext that way.
Anyone sees any error or has any other approach?
Thanks in advance.
This should work provided that the OpenRowDetailsCommand and SelectedIndex properties are defined in the view model class of the parent UserControl:
<Style TargetType="DataGridCell">
<Setter Property="Tag" Value="{Binding RelativeSource={RelativeSource AncestorType=UserControl}}" />
<Setter Property="ContextMenu">
<Setter.Value>
<ContextMenu>
<MenuItem Header="Open Details"
Command="{Binding PlacementTarget.Tag.DataContext.OpenRowDetailsCommand, RelativeSource={RelativeSource AncestorType=ContextMenu}}"
CommandParameter="{Binding PlacementTarget.Tag.DataContext.SelectedIndex, RelativeSource={RelativeSource AncestorType=ContextMenu}}"/>
</ContextMenu>
</Setter.Value>
</Setter>
</Style>
I like to use a BindingProxy for this (as described in this SO answer)
public class BindingProxy : Freezable
{
protected override Freezable CreateInstanceCore()
{
return new BindingProxy();
}
public object Data
{
get { return (object)GetValue(DataProperty); }
set { SetValue(DataProperty, value); }
}
public static readonly DependencyProperty DataProperty =
DependencyProperty.Register("Data", typeof(object),typeof(BindingProxy));
}
Use it like this:
<DataGrid ItemsSource="{Binding Model.Collection}">
<DataGrid.Resources>
<local:BindingProxy x:Key="VMProxy" Data="{Binding}" />
<local:BindingProxy x:Key="DataGridProxy" Data="{Binding RelativeSource={RelativeSource Self}}" />
<Style TargetType="DataGridCell">
<Setter Property="ContextMenu">
<Setter.Value>
<ContextMenu>
<MenuItem Header="Open Details"
Command="{Binding Data.OpenRowDetailsCommand, Source={StaticResource VMProxy}}"
CommandParameter="{Binding Data.SelectedIndex, Source={StaticResource DataGridProxy}}"/>
</ContextMenu>
</Setter.Value>
</Setter>
</Style>
</DataGrid.Resources>
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>
I have a button that when clicked, displays a ContextMenu. In this ContextMenu, I have MenuItems. If the MenuItem is left clicked, it should execute a command. All this behavior works properly as of now with the following code:
<Button.ContextMenu>
<ContextMenu>
<ContextMenu.ItemContainerStyle>
<Style TargetType="{x:Type MenuItem}">
<Setter Property="Command" Value="{Binding StartContextMenuCommand}" />
<Setter Property="CommandParameter" Value="{Binding RelativeSource={RelativeSource Self}, Path=Header}" />
</Style>
</ContextMenu.ItemContainerStyle>
<MenuItem Header="{x:Static Name:ContextMenuStartNames.1}"/>
<MenuItem Header="{x:Static Name:ContextMenuStartNames.2}"/>
<MenuItem Header="{x:Static Name:ContextMenuStartNames.3}"/>
<MenuItem Header="{x:Static Name:ContextMenuStartNames.4}"/>
</ContextMenu>
</Button.ContextMenu>
Now I want each menu item to have a context menu with one MenuItem. Since left clicking invokes a Command on a MenuItem, I want the right click behavior to display the following ContextMenu:
<ContextMenu>
<MenuItem Header="Set Default"></MenuItem>
</ContextMenu>
I tried putting this as a setter in the ItemContainerStyle. I also tried putting it as a ContextMenu of a MenuItem, but neither of my attempts, among others have worked. I'm sure I could come up with a hacky way to do this, but I want to keep it clean and simple.
Maybe i do not understand your question (my english is not... anything special):D But this should work if you just want multilevel contextmenu:
<ContextMenu>
<MenuItem Header="Top Level 1">
<MenuItem Header="Sub Level" />
<MenuItem Header="Sub Level" />
</MenuItem>
<MenuItem Header="Top Level 2">
<MenuItem Header="Sub Level" />
<MenuItem Header="Sub Level" />
</MenuItem>
</ContextMenu>
Here's my current fix to this issue, but I don't like it at all. I would like to avoid code behind as much as possible.
<ContextMenu.ItemContainerStyle>
<Style TargetType="{x:Type MenuItem}">
<Setter Property="Command" Value="{Binding StartCommand}" />
<Setter Property="CommandParameter" Value="{Binding RelativeSource={RelativeSource Self}, Path=Header}" />
<Setter Property="ContextMenu">
<Setter.Value>
<ContextMenu StaysOpen="True">
<MenuItem Header="Set As Default"/>
</ContextMenu>
</Setter.Value>
</Setter>
<EventSetter Event="PreviewMouseRightButtonUp" Handler="MenuItem_Click"/>
</Style>
</ContextMenu.ItemContainerStyle>
And the code behind:
private void MenuItem_Click(object sender, RoutedEventArgs e)
{
ButtonContextMenu.StaysOpen = true;
(sender as MenuItem).ContextMenu.IsOpen = true;
}
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>