Correctly set a context menu inside of a template - wpf

I am trying to setup a control template that allows me to add a context menu to an element. Trouble is the only way I can figure out how to do it is by nesting TWO LABELS.
This is my style:
<Style x:Key="RightClickMenu" TargetType="{x:Type Label}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Label">
<Label Name="RightClickElement">
<Label.ContextMenu>
<ContextMenu DataContext="{Binding PlacementTarget, RelativeSource={RelativeSource Self}}">
<MenuItem x:Name="CopyMenuItem" Command="{Binding DataContext.OnCopyCommand}" InputGestureText="Ctrl+C" Header="Copy" />
<MenuItem x:Name="PasteMenuItem" Command="{Binding DataContext.OnPasteCommand}" InputGestureText="Ctrl+P" Header="Paste" />
</ContextMenu>
</Label.ContextMenu>
</Label>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="true">
<Setter Property="FontWeight" Value="Bold"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Now the only way to get this into a label is by doing:
<Expander Name="ExpanderControl" Margin="5,5,5,5">
<Expander.Header>
<Label x:Name="LabelTemplate" Style="{StaticResource RightClickMenu}" />
</Expander.Header>
</Expander>
Which means I end up with which is a bit rubbish. Plus it means I cant define this context menu for other controls!
How can I specify that my expnder header has a template. Which is just a single label?

Related

WPF apply triggers to listview items to change background

I am trying to create a sort of left menu for navigation inside the desktopapplication. My idea is to use Buttons as Listview items which should behave in this way: when i hover with the mouseover them theri background should change color (becomes darkCyan) and when i click one its background color should change persistently (to darkCyan) until i click on another button of the list. The problem is that i am using a the DataTemplate property to specify how the buttons should look like and I am tryin to apply the triggers to change the background color on the ControlTemplate of the ListView. The result is that sometimes the background color changes but the command related to the button is not fired other times the contrary. I think that I am doing the things in the wrong element of the tree view, but I don't have enough knowledge of the tree view so I am not understanding what I am doing wrong. Here is the code of the XAML in which i define the styles for the Buttons and the ListView
<Window.Resources>
<DataTemplate DataType="{x:Type viewModels:TripViewModel}">
<views:TripView />
</DataTemplate>
<Style TargetType="{x:Type Button}">
<Setter Property="Height"
Value="50" />
<Setter Property="Background"
Value="#555D6F" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Border>
<Grid Background="Transparent">
<ContentPresenter HorizontalAlignment="Center"
VerticalAlignment="Center" />
</Grid>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style TargetType="{x:Type ListViewItem}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ListViewItem}">
<Border BorderBrush="Transparent"
BorderThickness="0"
Background="{TemplateBinding Background}">
<ContentPresenter HorizontalAlignment="Center"
VerticalAlignment="Center" />
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver"
Value="True">
<Setter Property="Background"
Value="DarkCyan" />
</Trigger>
<Trigger Property="IsSelected"
Value="True">
<Setter Property="Background"
Value="DarkCyan" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
And here is the code in which I create the ListView
<ListView Name="MenuButtons"
ItemsSource="{Binding PageViewModels}"
Background="Transparent"
IsSynchronizedWithCurrentItem="True">
<ListView.ItemTemplate>
<DataTemplate>
<Button Content="{Binding Name}"
Command="{Binding DataContext.ChangePageCommand, RelativeSource={RelativeSource AncestorType={x:Type Window}}}"
Margin="0" />
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
Anyone can help?
Thanks
I have solved the issue by using a ListBox instead of a ListView and setting the ItemContainer to be a button in the following way
<ListBox.ItemTemplate>
<ItemContainerTemplate>
<Button Content="{Binding Name}"
Command="{Binding DataContext.ChangePageCommand, RelativeSource={RelativeSource AncestorType={x:Type Window}}}"
Margin="0" />
</ItemContainerTemplate>
</ListBox.ItemTemplate>

ContextMenu commands on DataGrid Row do not work

I create a style than I bind it to a DataGrid.RowStyle Row style. The ContextMenu appears and I am able to select from the menu items, but the command won't execute. I tested the command with other control and works fine.
<Style x:Key="DataGridRow" TargetType="{x:Type DataGridRow}">
<Setter Property="ContextMenu">
<Setter.Value>
<ContextMenu DataContext="{Binding PlacementTarget.DataContext, RelativeSource={RelativeSource Self}}">
<MenuItem Header="Add" Command="{Binding AddMessageContextMenu_Command}"/>
<MenuItem Header="Edit" Command="{Binding EditMessage_Command}"/>
</ContextMenu>
</Setter.Value>
</Setter>
<Style>
<DataGrid Name="SearchTableDataGrid" ItemsSource="{Binding SearchTableDataGrid_ItemSource}"
<DataGrid.RowStyle>
<Style TargetType="{x:Type DataGridRow}">
<Style.Triggers>
<Trigger Property="AlternationIndex" Value="0">
<Setter Property="Background" Value="#FBFCFC" />
</Trigger>
<Trigger Property="AlternationIndex" Value="1">
<Setter Property="Background" Value="#f6f8f8" />
</Trigger>
</Style.Triggers>
</Style>
</DataGrid.RowStyle>
</DataGrid>
You need a reference to the right DataContext. As suggested in the comments, you can use the Rows Tag property for this.
Below the code to get to the Windows DataContext
<Style TargetType="{x:Type DataGridRow}">
<Setter Property="Tag"
Value="{Binding RelativeSource={RelativeSource AncestorType={x:Type Window}},
Path=DataContext}"/>
<Setter Property="ContextMenu">
<Setter.Value>
<ContextMenu DataContext="{Binding PlacementTarget.Tag,
RelativeSource={RelativeSource Self}}">
<MenuItem Header="Add" Command="{Binding AddMessageContextMenu_Command}"/>
<MenuItem Header="Edit" Command="{Binding EditMessage_Command}"/>
</ContextMenu>
</Setter.Value>
</Setter>
</Style>

How to set an Icon in a ContextMenu's MenuItem when using ItemContainerStyle

So I have a Context Menu And a MenuItem in it which breaks out into a list of names:
<ContextMenu>
<MenuItem Header="Set As Default For" ItemsSource="{Binding Source={StaticResource Names}}">
<MenuItem.ItemContainerStyle>
<Style TargetType="MenuItem">
<Setter Property="Header" Value={Binding Name}/>
<Setter Property="Command" Value="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=MenuItem}, Path=DataContext.DoSomething}" />
<Setter Property="CommandParameter" Value="{Binding }" />
</Style>
</MenuItem.ItemContainerStyle>
</MenuItem>
</ContextMenu>
Now the above code works file and displays my list of names. Now I would like to add an Icon next to each Name using a Pack URI.. So from this question I can see the best way to do it is to template the Header so I tried that first like the question
<ContextMenu>
<MenuItem Header="Set As Default For" ItemsSource="{Binding Source={StaticResource Names}}">
<MenuItem.ItemContainerStyle>
<Style TargetType="MenuItem">
<Setter Property="Header">
<Setter.Value>
<StackPanel>
<Image Width="20" Height="20" Source="/MyProj;component/Resources/MyImg.png" />
<ContentPresenter Content="{Binding Name}" />
</StackPanel>
</Setter.Value>
</Setter>
<Setter Property="Command" Value="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=MenuItem}, Path=DataContext.DoSomething}" />
<Setter Property="CommandParameter" Value="{Binding }" />
</Style>
</MenuItem.ItemContainerStyle>
</MenuItem>
</ContextMenu>
but this gave me the error:
Specified element is already the logical child of another element.
Disconnect it first.
So I after some research I tried:
<ContextMenu>
<MenuItem Header="Set As Default For" ItemsSource="{Binding Source={StaticResource Names}}">
<MenuItem.ItemContainerStyle>
<Style TargetType="MenuItem">
<Setter Property="Header">
<Setter.Value>
<ControlTemplate>
<StackPanel>
<Image Width="20" Height="20" Source="/MyProj;component/Resources/MyImg.png" />
<ContentPresenter Content="{Binding Name}" />
</StackPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
<Setter Property="Command" Value="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=MenuItem}, Path=DataContext.DoSomething}" />
<Setter Property="CommandParameter" Value="{Binding }" />
</Style>
</MenuItem.ItemContainerStyle>
</MenuItem>
</ContextMenu>
But now all my names are ControlTemplate and no icon is displayed...
How do I add an icon to a Context Menu's Menu Item through ItemContainerStyle?
EDIT
I've tried:
<Setter Property="Header" Value="{Binding Name}"/>
<Setter Property="Icon">
<Setter.Value>
<Image Width="20" Height="20" Source="/MyProj;component/Resources/MyImg.png" />
</Setter.Value>
</Setter>
And I get an icon rendering but only for the last item in my menu?
The problem is that you cannot use one visual in more then one place. You can do it either by using HeaderTemplate instead of Header, but use DataTemplate instead of ControlTemplate
<Style TargetType="MenuItem">
<Setter Property="HeaderTemplate">
<Setter.Value>
<DataTemplate>
<StackPanel>
<Image Width="20" Height="20" Source="/MyProj;component/Resources/MyImg.png" />
<ContentPresenter Content="{Binding Name}" />
</StackPanel>
</DataTemplate>
</Setter.Value>
</Setter>
<!-- other setters -->
</Style>
Note that this solution will put icon in the content part of MenuItem, not the icon. Another solution is to set Icon property of MenuItem as another Setter but in this case Image needs to
be separate Resource with x:Shared set to false otherwise you'll end up with the same problem where only last item has an icon.
<ContextMenu>
<ContextMenu.Resources>
<Image Width="20" Height="20" Source="/MyProj;component/Resources/MyImg.png" x:Key="myMenuIcon" x:Shared="False" />
</ContextMenu.Resources>
<MenuItem Header="Set As Default For" ItemsSource="{Binding Source={StaticResource Names}}">
<MenuItem.ItemContainerStyle>
<Style TargetType="{x:Type MenuItem}">
<Setter Property="Header" Value="{Binding Name}"/>
<Setter Property="Icon" Value="{StaticResource myMenuIcon}"/>
</Style>
</MenuItem.ItemContainerStyle>
</MenuItem>
</ContextMenu>

Customizing Context Menu in WPF

I have a project here where I require customizing the context menu in my WPF application in which a button would be placed at the bottom of all the menuitems.
However, if I were to add the button through the XAML, it would appear as another item in the collection in the context menu and the mouse-over highlight would act on it.
I would like to have the context menu tuned to a grid-like style whereby I could customize the style underneath it.
Any idea how I can achieve this (preferably in the XAML)?
It's actually pretty straightforward in the XAML.
Just define it under the element for which you want to have the context menu.
<Border>
<Border.ContextMenu>
<ContextMenu>
<ContextMenu.Template>
<ControlTemplate>
<Grid>
<!--Put anything you want in here.-->
</Grid>
</ControlTemplate>
</ContextMenu.Template>
</ContextMenu>
</Border.ContextMenu>
</Border>
For your menu item style with the button in the item you can use the following code:
Note - Adding items to the Header will keep it in the same MenuItem, but if added to the MenuItem only it will be regarded as a new MenuItem.
<ContextMenu>
<ContextMenu.Items>
<MenuItem>
<MenuItem.Header>
<StackPanel>
<TextBlock Text="Item 1"/>
<Button Content="Button 1" Margin="5"/>
</StackPanel>
</MenuItem.Header>
</MenuItem>
<MenuItem>
<MenuItem.Header>
<StackPanel>
<TextBlock Text="Item 2"/>
<Button Content="Button 2" Margin="5"/>
</StackPanel>
</MenuItem.Header>
</MenuItem>
</ContextMenu.Items>
</ContextMenu>
This will be the resulting ContextMenu:
From there you can style the MenuItem or Button etc.
Hope it helps!
You can start with the example style/template (from here) for ContextMenu and adjust it to your needs.
<Style TargetType="{x:Type ContextMenu}">
<Setter Property="SnapsToDevicePixels" Value="True" />
<Setter Property="OverridesDefaultStyle" Value="True" />
<Setter Property="Grid.IsSharedSizeScope" Value="true" />
<Setter Property="HasDropShadow" Value="True" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ContextMenu}">
<Border x:Name="Border"
Background="{StaticResource MenuPopupBrush}"
BorderThickness="1">
<Border.BorderBrush>
<SolidColorBrush Color="{StaticResource BorderMediumColor}" />
</Border.BorderBrush>
<StackPanel IsItemsHost="True"
KeyboardNavigation.DirectionalNavigation="Cycle" />
</Border>
<ControlTemplate.Triggers>
<Trigger Property="HasDropShadow" Value="true">
<Setter TargetName="Border" Property="Padding" Value="0,3,0,3" />
<Setter TargetName="Border" Property="CornerRadius" Value="4" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>

I just need a simple WPF ToolTip Style that displays multiple lines

Due to all the noise about fancy, super, huge, and blah, blah, blah, tooltips, I cannot find the answer.
I just need a simple style that sets TextWrapping="Wrap" and allows me to set a width.
One that duplicates the existing / default style, but just word wraps.
<Window.Resources>
<Style TargetType="{x:Type ToolTip}">
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<TextBlock TextWrapping="Wrap" Text="{Binding}" />
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
<Grid>
<Rectangle Width="100" Height="100" Fill="Red">
<Rectangle.ToolTip>
<ToolTip Width="100">
This is some text with text wrapping.
</ToolTip>
</Rectangle.ToolTip>
</Rectangle>
</Grid>
This example is assuming you want to be able to set the width on a per-usage basis. If you want to set it as part of the style, add it to the TextBlock element.
This style prevents a tooltip from popping up on empty strings.
<Style TargetType="ToolTip">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ToolTip">
<TextBlock Text="{TemplateBinding Content}"
MaxWidth="400"
TextWrapping="Wrap"/>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="Content" Value="">
<Setter Property="Visibility" Value="Collapsed" />
</Trigger>
</Style.Triggers>
</Style>
Or using ContentTemplate:
<Style TargetType="{x:Type ToolTip}">
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding}"
MaxWidth="400"
TextWrapping='Wrap' />
</StackPanel>
</DataTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="Content" Value="">
<Setter Property="Visibility" Value="Collapsed" />
</Trigger>
</Style.Triggers>
</Style>
If you just want to get the effects below, have a read at this post.

Resources