I'm beginner in MVVM and I've been trying to display MenuItems in a ContextMenu, but something seems to be wrong. I've already made a working Menu which has Itemssource bound to a collection in the viewmodel, but it doesn't work in case of the ContextMenu.
Could you guys take a look and say if I'm missing something?
Here's the Menu, which works well:
<Menu DockPanel.Dock="right" ItemsSource="{Binding Path=modes}" Background="#FF303030" Foreground="White" Height="21">
<Menu.Resources>
<Style TargetType="{x:Type MenuItem}">
<Setter Property="Background" Value="#FF303030"/>
<Setter Property="Foreground" Value="White"/>
<Setter Property="Height" Value="21" />
<Setter Property="Visibility" Value="{Binding visibility}" />
<Setter Property="Header" Value="{Binding header}" />
<Setter Property="ItemsSource" Value="{Binding children}"/>
<Setter Property="Command" Value="{Binding action}"/>
<Setter Property="CommandParameter" Value="{Binding attachedData}"/>
</Style>
</Menu.Resources>
</Menu>
And here's the XAML of the ContextMenu, which, for some reason, doesn't display any MenuItems:
<Grid x:Name="btnMode" PreviewMouseDown="OnBtnModeMouseDown" Width="19" Height="19" Margin="0,0" DockPanel.Dock="Right">
<Canvas>
<Path Data="M0,0 L19,0 L19,19 L0,19" Margin="0,0" Fill="#FF303030"/>
<Ellipse Height="12" Margin="4,3,0,0" Width="12" Fill="#FFFDFDFD"/>
<Path Data="M1.5,1L18.5,1 L10,9.5" Margin="0,0,0,8.875" Fill="#FF303030"/>
</Canvas>
<Grid.ContextMenu>
<ContextMenu ItemsSource="{Binding Path=modes}">
<ContextMenu.ItemContainerStyle>
<Style TargetType="MenuItem">
<Setter Property="Background" Value="#FF303030"/>
<Setter Property="Foreground" Value="White"/>
<Setter Property="Height" Value="21" />
<Setter Property="Visibility" Value="{Binding visibility}" />
<Setter Property="Header" Value="{Binding header}" />
<Setter Property="ItemsSource" Value="{Binding children}"/>
<Setter Property="Command" Value="{Binding action}"/>
<Setter Property="CommandParameter" Value="{Binding attachedData}"/>
</Style>
</ContextMenu.ItemContainerStyle>
</ContextMenu>
</Grid.ContextMenu>
</Grid>
I also have a ContextMenu that has hard-coded menu items:
<Grid x:Name="btnMenu" PreviewMouseDown="OnBtnMenuMouseDown" Width="19" Height="19" Margin="0,1" DockPanel.Dock="Right">
<Canvas>
<Path Data="M0,0 L19,0 L19,19 L0,19" Margin="0,0" Fill="#FF303030"/>
<Path Data="M0,0L7,0 L3.5,7" Margin="6,7" Fill="White"/>
</Canvas>
<Grid.ContextMenu >
<ContextMenu IsVisibleChanged="OnBtnMenuPopup">
<MenuItem Name="menuFloatingWindow" Header="Floating window" Click="OnDockingMenu"></MenuItem>
<MenuItem Name="menuDockedWindow" Header="Docked window" Click="OnDockingMenu"></MenuItem>
<MenuItem Name="menuTabbedDocument" Header="Tabbed document" Click="OnDockingMenu"></MenuItem>
<MenuItem Name="menuAutoHide" Header="Auto hide" Click="OnDockingMenu"></MenuItem>
<MenuItem Name="menuClose" Header="Close" Click="OnDockingMenu"></MenuItem>
</ContextMenu>
</Grid.ContextMenu>
</Grid>
The collection of MenuItems is an ObservableCollection, defined as follows:
_menus = new ObservableCollection<MenuItem>()
{
new MenuItem(
"File", // string header
Visibility.Visible, // Visibility visibility
null, // ICommand action
null, // object attachedData (CommandParameters)
new ObservableCollection<MenuItem>() // children
{
new MenuItem(
"Exit",
Visibility.Visible,
MainWindow.exitCommand,
null,
null
)
}
),
..., // more nested MenuItems here
};
Both the working Menu and the ContextMenu have the same ObservableCollection as an ItemSource.
Here's an image showing how the controls look in the application:
Did I mess something up in XAML?
P.S.
I tried debugging the XAML at runtime using this: http://www.wpftutorial.net/DebugDataBinding.html but I couldn't see any helpful information when the breakpoint occured.
Related
I have a wpf datagrid, by a template I create a contextmenu for every row.
Since the grid rows are getting constantly changed it often happens that the contextmenu gets closed when the focused row disappears.
After a while the contextmenu won't open anymore on the entire grid. It happens if the contextmenu is heavily used and there are lots of updates.
I tried to debug this by logging the ContextMenuOpening, contextMenuClosing and MouseRightButtonDown events. The "opening" gets raised but the menu isn't visible. Reopening the window clears the issue.
Currently I am at odds. Can somebody give me some pointers on how to resolve this issue?
Perhaps somebody could recommend some XAML optimization.
In the .cs file there is no interaction with the contextmenu, just adding and removing of the rows and some view-refresh (to update the grouping). The events are just for logging.
The ressource part
<Window.Resources>
<local:NegativeColorConverter x:Key="NegativeColorConverter"/>
<local:AllJobs x:Key="jobs"/>
<CollectionViewSource x:Name="cvsAllJobs" x:Key="cvsJobs" Source="{StaticResource jobs}" IsLiveGroupingRequested="True" IsLiveSortingRequested="True" CollectionViewType="ListCollectionView">
<CollectionViewSource.LiveGroupingProperties>
<s:String>omitted</s:String>
</CollectionViewSource.LiveGroupingProperties>
<CollectionViewSource.GroupDescriptions>
<PropertyGroupDescription PropertyName="omitted"/>
</CollectionViewSource.GroupDescriptions>
<CollectionViewSource.SortDescriptions>
<scm:SortDescription PropertyName="GroupSort"/>
</CollectionViewSource.SortDescriptions>
</CollectionViewSource>
</Window.Resources>
And the grid
<DataGrid
VirtualizingPanel.IsVirtualizingWhenGrouping="True"
x:Name="gridJobs"
CanUserSortColumns="False"
IsReadOnly="True"
CanUserDeleteRows="False"
CanUserAddRows="False"
GridLinesVisibility="Horizontal"
DragEnter="gridJobs_DragEnter"
Drop="gridJobs_Drop"
AllowDrop="True"
AutoGenerateColumns="False"
Margin="10"
SelectedItem ="{Binding SelectedRow, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
ItemsSource="{Binding Source={StaticResource cvsJobs}}"
HorizontalGridLinesBrush="Transparent"
PreviewKeyDown="gridJobs_PreviewKeyDown"
ContextMenuClosing="gridJobs_ContextMenuClosing"
ContextMenuOpening="gridJobs_ContextMenuOpening"
MouseRightButtonDown="gridJobs_MouseRightButtonDown"
>
<DataGrid.Resources>
<tools:BindingProxy x:Key="proxy" Data="{Binding}" />
<ContextMenu StaysOpen="True" x:Key="Jobs" ItemsSource="{Binding MenuItems}">
<ContextMenu.ItemContainerStyle>
<Style TargetType="{x:Type MenuItem}">
<Setter Property="Command" Value="{Binding Command}" />
<Setter Property="IsEnabled" Value="{Binding Enabled}" />
</Style>
</ContextMenu.ItemContainerStyle>
<ContextMenu.ItemTemplate>
<HierarchicalDataTemplate DataType="MenuItemViewModel" ItemsSource="{Binding MenuItems}">
<TextBlock Text="{Binding Header}"/>
</HierarchicalDataTemplate>
</ContextMenu.ItemTemplate>
</ContextMenu>
</DataGrid.Resources>
Columns omitted
<DataGrid.CellStyle>
<Style TargetType="DataGridCell">
<Setter Property="BorderThickness" Value="0"/>
<Setter Property="FontSize" Value="14"/>
<Setter Property="FontFamily" Value="Calibri"/>
<Setter Property="VerticalAlignment" Value="Center"/>
<Style.Triggers>
<Trigger Property="IsSelected" Value="True">
<!--<Setter Property="Background" Value="Black"/>-->
<Setter Property="Background" Value="{Binding BrushState, Mode=OneWay, UpdateSourceTrigger=PropertyChanged, Converter={StaticResource NegativeColorConverter}}"/>
<Setter Property="Foreground" Value="{Binding BrushState, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}"/>
</Trigger>
</Style.Triggers>
</Style>
</DataGrid.CellStyle>
<DataGrid.RowStyle>
<Style TargetType="DataGridRow" >
<Setter Property="ContextMenu" Value="{StaticResource Jobs}" />
<Setter Property="Foreground" Value="{Binding BrushState, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}"/>
<Setter Property="Margin" Value="10,0,0,0"/>
<Setter Property="BorderBrush" Value="Transparent"/>
<Setter Property="Height" Value="20"/>
<Setter Property="BorderThickness" Value="0"/>
</Style>
</DataGrid.RowStyle>
<DataGrid.GroupStyle>
<GroupStyle>
<GroupStyle.HeaderTemplate>
<DataTemplate>
<DockPanel Background="Gray" Height="1">
</DockPanel>
</DataTemplate>
</GroupStyle.HeaderTemplate>
</GroupStyle>
</DataGrid.GroupStyle>
</DataGrid>
Add to the ContextMenu tag an attribute x:Shared="false", this will create an own ContextMenu for each row. So it should work I think.
<ContextMenu StaysOpen="True" x:Key="Jobs" ItemsSource="{Binding MenuItems}" x:Shared="false">
My problem is when I click or hover in ListViewItem which also show the silver background:
enter image description here
this is my code xaml:
<ListView
Margin="0,30,0,0"
Height="600"
ScrollViewer.VerticalScrollBarVisibility="Hidden"
ScrollViewer.HorizontalScrollBarVisibility="Disabled"
Name="ListViewFC" ItemsSource="{Binding ListWords, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
SelectedItem="{Binding SelectedItem, Mode=TwoWay}" >
<ListView.ItemTemplate>
<DataTemplate>
<Border
Width="340"
x:Name="Border"
Height="80"
Background="Pink"
CornerRadius="15"
BorderThickness="1"
>
<Grid>
<TextBlock
VerticalAlignment="Center"
x:Name="txtContent"
Foreground="Black"
Text="{Binding Question1,UpdateSourceTrigger=PropertyChanged,Mode=TwoWay}"
TextWrapping="NoWrap"
Margin="30 0 0 0"
FontSize="15"
/>
</Grid>
</Border>
<DataTemplate.Triggers>
<DataTrigger
Binding="{Binding RelativeSource={RelativeSource FindAncestor,
AncestorType={x:Type ListViewItem}}, Path=IsSelected}" Value="True">
<Setter TargetName="Border" Property="Background" Value="White" />
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
I want that When I hover or click It don't show the silver background.
pls, help me .Thanks.
Add this inside your ListView:
<ListView.Resources>
<Style BasedOn="{StaticResource TextBlockStyle}" TargetType="{x:Type TextBlock}" />
<Style BasedOn="{StaticResource ListViewItemStyle}" TargetType="{x:Type ListViewItem}" />
</ListView.Resources>
Then add this outside of your ListView (this displays a background of gold on hover):
<ListView.ItemContainerStyle>
<Style TargetType="{x:Type ListViewItem}">
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="Gold" />
</Trigger>
</Style.Triggers>
</Style>
</ListView.ItemContainerStyle>
The default ControlTemplate of the ListView contains a Border with Padding of 2X. Hence you have to change its template something like this
<ListView.ItemContainerStyle>
<Style TargetType="{x:Type ListViewItem}">
<Setter Property="Background" Value="Transparent" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ListViewItem}">
<ContentPresenter />
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ListView.ItemContainerStyle>
So I am trying to create a click event for one of my contextmenu items but it's throwing me an error I have never seen before.
There wasnt too much information on Google and the information I saw didnt help me because I didnt understand it.
The line throwing the error is this one
GridView ColumnHeaderContainerStyle="{StaticResource GridViewColumnHeaderStyle1}"
saying
'cmndOP_Click' is not valid. 'Click' is not an event on
'System.Windows.Controls.GridView'.
Reading the error message tells me that 'Click' is not a valid event in the gridview, but its not in the gridview its in the listviewitem.
Whats causing this error and what should I do in the future to prevent this?
<ListView Margin="10,36,520,10" ScrollViewer.HorizontalScrollBarVisibility="Hidden" BorderBrush="Black" Padding="-1, -1, 1, 0" Background="Transparent" BorderThickness="1.000001" Name="lvUsers" Style="{DynamicResource ListViewStyle1}">
<ListView.ItemContainerStyle>
<Style TargetType="{x:Type ListViewItem}">
<Setter Property="ContextMenu">
<Setter.Value>
<ContextMenu>
<MenuItem Name="cmndOP" Header="OP User" Click="cmndOP_Click"/>
<MenuItem Header="Kick User"/>
<MenuItem Header="Ban User"/>
<MenuItem Header="Send Command"/>
</ContextMenu>
</Setter.Value>
</Setter>
<Setter Property="Background" Value="Transparent" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ListViewItem}">
<Border
BorderBrush="Transparent"
BorderThickness="0"
Background="{TemplateBinding Background}">
<GridViewRowPresenter HorizontalAlignment="Stretch" VerticalAlignment="{TemplateBinding VerticalContentAlignment}" Width="Auto" Margin="0" Content="{TemplateBinding Content}"/>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Foreground" Value="White" />
<Setter Property="Background" Value="#696969" />
<Setter Property="BorderBrush" Value="#696969" />
<Setter Property="BorderThickness" Value="0" />
</Trigger>
<Trigger Property="IsSelected" Value="True">
<Setter Property="Foreground" Value="White" />
<Setter Property="Background" Value="#696969" />
<Setter Property="BorderBrush" Value="#696969" />
<Setter Property="BorderThickness" Value="0" />
</Trigger>
</Style.Triggers>
</Style>
</ListView.ItemContainerStyle>
<ListView.View>
<GridView ColumnHeaderContainerStyle="{StaticResource GridViewColumnHeaderStyle1}">
<GridViewColumn x:Name="GridViewColumnName" Header="Name" Width="165">
<GridViewColumn.CellTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Image x:Name="Image_GridViewColumnName" Width="16" Height="16" Source="Images\minecraft.png" />
<Label Content="{Binding Username}" Visibility="Visible" />
</StackPanel>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
</GridView>
</ListView.View>
</ListView>
And this behind it
private void cmndOP_Click(object sender, RoutedEventArgs e)
{
Commands cmd = new Commands();
cmd.OpUser(lvUsers.SelectedItem.ToString());
}
EDIT
This is where the error happands.
This is also in the App.xaml because otherwise I would get another error
<Style x:Key="GridViewColumnHeaderStyle1" TargetType="{x:Type GridViewColumnHeader}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type GridViewColumnHeader}">
<Border BorderThickness="0,0,0,1" BorderBrush="Black" Background="Transparent">
<TextBlock x:Name="ContentHeader" Text="{TemplateBinding Content}" Padding="5,5,5,5" Width="{TemplateBinding Width}" TextAlignment="Center" />
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
<Setter Property="OverridesDefaultStyle" Value="True" />
<Setter Property="Foreground" Value="Black" />
<Setter Property="FontFamily" Value="Segoe UI" />
<Setter Property="FontSize" Value="12" />
</Style>
I was surprised to find that your error was reproducible from the XAML you provided. When I define the context menu as a resource and update the setter accordingly, I no longer get the error.
<ListView Margin="10,36,520,10" ScrollViewer.HorizontalScrollBarVisibility="Hidden" BorderBrush="Black" Padding="-1, -1, 1, 0" Background="Transparent" BorderThickness="1.000001" Name="lvUsers" >
<ListView.Resources>
<ContextMenu x:Key="ListViewItemContextMenu">
<MenuItem Name="cmndOP" Header="OP User" Click="cmndOP_Click" />
<MenuItem Header="Kick User"/>
<MenuItem Header="Ban User"/>
<MenuItem Header="Send Command"/>
</ContextMenu>
</ListView.Resources>
<ListView.ItemContainerStyle>
<Style TargetType="{x:Type ListViewItem}">
<Setter Property="ContextMenu" Value="{StaticResource ListViewItemContextMenu}" />
I have code (below) that should produce a styled context menu. Unfortunately the menu item also shows a "white" bit that shouldn't be there (see below). I have tried styling it out but it doesn't work. Any help would be appreciated
<Style TargetType="ContextMenu">
<Setter Property="Background" Value="{StaticResource backgroundDark}" />
<Setter Property="BorderBrush" Value="{StaticResource highlight}" />
<Style.Resources>
<Style TargetType="MenuItem">
<Setter Property="Foreground" Value="Black" />
<Setter Property="Background" Value="Transparent" />
<Style.Triggers>
<Trigger Property="IsHighlighted" Value="True">
<Setter Property="Foreground" Value="{StaticResource highlightLight}" />
</Trigger>
</Style.Triggers>
</Style>
</Style.Resources>
</Style>
you will want to override the
StackPanel Margin of MenuItems
and then override with the same value the ItemsPanel
so basically you can just add 2 simple styles in addition to your style.
I have added a sample:
<Grid>
<Grid.Resources>
<ItemsPanelTemplate x:Key="GlobalMenuItemPanelTemplate">
<StackPanel Margin="-25,0,0,0" Background="White"/>
</ItemsPanelTemplate>
<Style TargetType="{x:Type ContextMenu}">
<Setter Property="ItemsPanel" Value="{StaticResource GlobalMenuItemPanelTemplate}"/>
</Style>
</Grid.Resources>
<Label Background="Bisque" Content="Right Click it" VerticalAlignment="Center" HorizontalAlignment="Center">
<Label.ContextMenu>
<ContextMenu>
<MenuItem Header="Menu item 1" />
<MenuItem Header="Menu item 2" />
<Separator />
<MenuItem Header="Menu item 3" />
</ContextMenu>
</Label.ContextMenu>
</Label>
</Grid>
I use the following XAML to achieve a menu using radio buttons:
<Grid.Resources>
<Style TargetType="{x:Type RadioButton}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type RadioButton}">
<TextBlock Text="{TemplateBinding Content}">
<TextBlock.Style>
<Style TargetType="{x:Type TextBlock}">
<Setter Property="Foreground" Value="White" />
<Style.Triggers>
<DataTrigger Binding="{Binding IsChecked, RelativeSource={RelativeSource AncestorType={x:Type RadioButton}}}" Value="True">
<Setter Property="Foreground" Value="Gold" />
</DataTrigger>
<DataTrigger Binding="{Binding IsMouseOver, RelativeSource={RelativeSource AncestorType={x:Type RadioButton}}}" Value="True">
<Setter Property="Foreground" Value="Gold" />
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Grid.Resources>
<StackPanel Margin="25,74,644,78" Background="{x:Null}">
<RadioButton Content="1. Do something." Click="RadioButton1_Click" FontSize="16" Margin="5"/>
<RadioButton Content="2. Do something else." Click="RadioButton2_Click" FontSize="16" Margin="5"/>
</StackPanel>
This works fine but I want to include an image as a menu option instead of text. (The image is for 'Home' and it will have a normal image and a hover over image)
Here is what I want to achieve:
How do I do this?
You can define another style for displaying Radio Button as an Image based on the existing style posted in question, just need slight changes in ControlTemplate's content part. Following is an example of such a Style and the usage :
<StackPanel Orientation="Horizontal" Background="Black">
<StackPanel.Resources>
<Style x:Key="PictSTyle" TargetType="{x:Type RadioButton}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type RadioButton}">
<Image Width="50" Height="50">
<Image.Style>
<Style TargetType="{x:Type Image}">
<Setter Property="Source" Value="home_default.png" />
<Style.Triggers>
<DataTrigger Binding="{Binding IsMouseOver, RelativeSource={RelativeSource AncestorType={x:Type RadioButton}}}" Value="True">
<Setter Property="Source" Value="home_hovered.png" />
</DataTrigger>
</Style.Triggers>
</Style>
</Image.Style>
</Image>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</StackPanel.Resources>
<RadioButton Margin="5" Style="{StaticResource ResourceKey=PictSTyle}"/>
</StackPanel>
To give a menu item an image:
<MenuItem>
<MenuItem.Icon>
<Image Source=""/>
</MenuItem.Icon>
</MenuItem>
for your trigger just change the MenuItem's icon to the "highlighted one", when the user hovers over the menu item (IsMouseOver), by changing the Icon's image source.
Put an image element as the "Content" property for your radio button
<RadioButton Click=HomRadioButtonClick>
<RadioButton.Content>
<Image Source=""/>
</RadioButton.Content>
</RadioButton>
You'll need some extra logic for the hover behavior, but that should at least get the image in your menu.