Databound ContextMenu Separator not honoring global Separator Style - c#

I have a context menu bound to a data source. For this context menu I have a DataTrigger to display a separator if the databound object has a value of "True" for the Separator property. This works well however it doesn't seem to pick up my global style for separators that I have in my application. The new separator appearance is different than the rest of my menu's. Is there a way to have it use the global style?
Below is the ContextMenu definition:
<ContextMenu
x:Key="ActionMenu"
ItemsSource="{Binding Source={StaticResource ActionMenuSource}}">
<ContextMenu.ItemContainerStyle>
<Style TargetType="{x:Type MenuItem}" BasedOn="{StaticResource {x:Type MenuItem}}">
<Style.Triggers>
<DataTrigger Binding="{Binding Path=Separator}" Value="true">
<Setter Property="MenuItem.Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type MenuItem}">
<Separator Style="{DynamicResource {x:Static
MenuItem.SeparatorStyleKey}}"/>
</ControlTemplate>
</Setter.Value>
</Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</ContextMenu.ItemContainerStyle>
</ContextMenu>
Here are my global values for defining the Separator.
<Style x:Key="{x:Static MenuItem.SeparatorStyleKey}" TargetType="{x:Type Separator}">
<Setter Property="Template" Value="{DynamicResource tmp_ManhMenuItemSeparator}"/>
</Style>
<ControlTemplate x:Key="tmp_ManhMenuItemSeparator" TargetType="{x:Type Separator}">
<Rectangle Name="SepRect" StrokeThickness="1" Stroke="White"/>
</ControlTemplate>

To make a style global the key needs to be the same as the TargetType which is clearly not the case in your sample. Change your key to "{x:Type Separator}" and see if that works for you. You can also merge your two snippets together unless you have a particular need to split them out, e.g.
<Style x:Key="{x:Type Separator}" TargetType="{x:Type Separator}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Separator}">
<Rectangle Name="SepRect" StrokeThickness="1" Stroke="White"/>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Once you have done that you can probably strip down the first block of code. It looks like you are trying to make the menu manually use the style. If you make it global you don't need to do that, e.g.
<ContextMenu x:Key="ActionMenu" ItemsSource="{Binding Source={StaticResource ActionMenuSource}}"/>

Related

How do I access an element of a parent template style in XAML?

I am having trouble accessing a named element of a parent template style in WPF.
I have a custom control for a Button (written per this StackOverflow question):
CustomButton.xaml:
<ResourceDictionary ...>
<Style x:Key="ButtonCustomStyle" TargetType="{x:Type local:ImageButton}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:ImageButton}">
<Grid>
<Image Source="{TemplateBinding Image}" Stretch="Fill" x:Name="CurrentImage"/>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter TargetName="CurrentImage" Property="Source" Value="{Binding ImageHover, RelativeSource={RelativeSource TemplatedParent}}"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
MainWindow.xaml:
<Window ...>
<Window.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="CustomButton.xaml"/>
</ResourceDictionary.MergedDictionaries>
<Style TargetType="{x:Type local:ImageButton}" BasedOn="{StaticResource ButtonCustomStyle}"/>
</ResourceDictionary>
</Window.Resources>
<Grid>
<local:ImageButton Image="\Images\image.png" ImageHover="\Images\image_hover.png" Width="20" Height="20"/>
</Grid>
ImageButton inherits Button, and Image and ImageHover are defined as dependency properties. This code works great and does what expected.
Now, I want to extend this template style for a single particular button, adding an additional Trigger for changing the image of the button. I am trying to do the following:
<Window...>
<Window.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="CustomButton.xaml"/>
</ResourceDictionary.MergedDictionaries>
<Style TargetType="{x:Type local:ImageButton}" BasedOn="{StaticResource ButtonCustomStyle}"/>
<Style TargetType="{x:Type local:ImageButton}"
BasedOn="{StaticResource ButtonCustomStyle}"
x:Key="Disableable">
<Style.Triggers>
<Trigger Property="IsEnabled" Value="False">
<Setter TargetName="CurrentImage" Property="Source" Value="\Images\disabled.png"/>
</Trigger>
</Style.Triggers>
</Style>
</ResourceDictionary>
</Window.Resources>
<Grid>
<local:ImageButton Image="\Images\image.png" ImageHover="\Images\image_hover.png" Width="20" Height="20"/>
<local:ImageButton Style="{StaticResource Disableable}" Image="\Images\image.png" ImageHover="\Images\image_hover.png" Width="20" Height="20"/>
</Grid>
But Visual Studio underscores the new setter line and says 'The name "CurrentImage" is not recognized'.
Is there a way to alter the CurrentImage element?
I am having trouble accessing a named element of a parent template style in WPF.
This is not possible. You can't base a ControlTemplate on another ControlTemplate.
I am afraid you will have to re-define the entire ControlTemplate from scratch in the derived Style if you want the setter to be able find the CurrentImage element that is defined in the template of the base Style.
WPF: Is there a way to override part of a ControlTemplate without redefining the whole style?
But instead of using a ControlTemplate trigger, you should be able to use a Style trigger that sets the Image property of the control itself rather than setting the Source property of the Image element in the template:
<Style x:Key="ButtonCustomStyle" TargetType="{x:Type local:ImageButton}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:ImageButton}">
<Grid>
<Image Source="{TemplateBinding Image}" Stretch="Fill" x:Name="CurrentImage"/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Image" Value="{Binding ImageHover, RelativeSource={RelativeSource Self}}"/>
</Trigger>
</Style.Triggers>
</Style>
<Style TargetType="{x:Type local:ImageButton}"
BasedOn="{StaticResource ButtonCustomStyle}"
x:Key="Disableable">
<Style.Triggers>
<Trigger Property="IsEnabled" Value="False">
<Setter Property="Image" Value="\Images\disabled.png"/>
</Trigger>
</Style.Triggers>
</Style>
Unlike ControlTemplete trigger, Style trigger doesn't have access to template elements. since you exposed Image property, use it in Style trigger setter:
<Trigger Property="IsEnabled" Value="False">
<Setter Property="Image" Value="\Images\disabled.png"/>
</Trigger>

WPF ControlTemplate inherit style from parent resources

I'm styling hyperlinks that appear within a border with the style "FooterPanel" as follows:
<Style x:Key="FooterPanel" TargetType="{x:Type Border}">
<Style.Resources>
<Style TargetType="{x:Type Hyperlink}">
<Setter Property="Foreground" Value="{StaticResource FooterPanelLinkBrush}"/>
</Style>
</Style.Resources>
</Style>
I also now have created a style to create a button as a hyperlink (so I can get properties such as IsDefault and IsCancel on a hyperlink):
<Style x:Key="LinkButton" TargetType="{x:Type Button}">
<Setter Property="Focusable" Value="False"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<TextBlock HorizontalAlignment="Center" VerticalAlignment="Center">
<Hyperlink Command="{TemplateBinding Command}" CommandParameter="{TemplateBinding CommandParameter}">
<Run Text="{TemplateBinding Content}"/>
</Hyperlink>
</TextBlock>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Normal hyperlinks within FooterPanel receive the FooterPanelLinkBrush foreground, but if I use LinkButton within a FooterPanel, the style is not applied. Is there a way to get the ControlTemplate to inherit the styles within the FooterPanel instead of any global hyperlink styles?
Edit:
According to this answer https://stackoverflow.com/a/9166963/2383681 there is special handling that means the Hyperlink won't receive the styles defined in the FooterPanel as it's not derived from Control.
I'm not sure what I'm trying to do is therefore possible without some code-behind, so I think I'm just going to workaround this and create a new style for FooterPanelLinkButton and explicitly reference this for the buttons that are in a footer panel. It would be interesting to know if this is possible however without doing that.
You can create a separate Style for the HyperLink:
<Style x:Key="FooterPanelLink" TargetType="{x:Type Hyperlink}">
<Setter Property="Foreground" Value="{StaticResource FooterPanelLinkBrush}"/>
</Style>
And then use this Style in the Resources of the FooterPanel and LinkButton styles in the following way:
<Style x:Key="FooterPanel" TargetType="{x:Type Border}">
<Style.Resources>
<Style TargetType="Hyperlink" BasedOn="{StaticResource FooterPanelLink}" />
</Style.Resources>
</Style>
<Style x:Key="LinkButton" TargetType="{x:Type Button}">
<Style.Resources>
<Style TargetType="Hyperlink" BasedOn="{StaticResource FooterPanelLink}" />
</Style.Resources>
<Setter Property="Focusable" Value="False"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<TextBlock HorizontalAlignment="Center" VerticalAlignment="Center">
<Hyperlink Command="{TemplateBinding Command}" CommandParameter="{TemplateBinding CommandParameter}">
<Run Text="{TemplateBinding Content}"/>
</Hyperlink>
</TextBlock>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
This way the HyperLink inside the LinkButton will use the color you assigned in the FooterPanelLink style.

Changing WPF Listbox SelectedItem text color and highlight/background Color using C#

I am trying to change the highlighted(selected) color and the highlighted text color of a wpf listbox at runtime. I have tried creating a style and applying it as follows:
Style s = new Style(typeof(ListBox));
s.Resources.Add(SystemColors.HighlightBrushKey, Setting.ListSelectedColor);
s.Resources.Add(SystemColors.HighlightTextBrushKey, Setting.ListSelectedTextColor);
lstGames.Style = s;
But this seems to do nothing. Is there any way to achieve this?
EDIT:
Per suggestions, I tried using DynamicResources to achieve this, but so far this has not been successful either. My code for this:
DYNAMICRESOURCES
<UserControl.Resources>
<Color x:Key="ListTextSelectedColor"/>
<Color x:Key="ListSelectedColor"/>
</UserControl.Resources>
LISTBOX
<ListBox ScrollViewer.HorizontalScrollBarVisibility="Hidden" ScrollViewer.VerticalScrollBarVisibility="Hidden"
Name="lstGames" Margin="20" Grid.Row="2" Grid.Column="2"
SelectionChanged="lstGames_SelectionChanged" Grid.RowSpan="2" Grid.ColumnSpan="2"
Background="{x:Null}" BorderBrush="{x:Null}" SelectionMode="Single"
FontSize="18" FontFamily="OCR A Extended">
<Style TargetType="ListBox">
<Style.Resources>
<SolidColorBrush x:Key="{x:Static SystemColors.HighlightBrushKey}" Color="{DynamicResource ListSelectedColor}"/>
<SolidColorBrush x:Key="{x:Static SystemColors.ControlBrushKey}" Color="{DynamicResource ListSelectedColor}"/>
<SolidColorBrush x:Key="{x:Static SystemColors.HighlightTextBrushKey}" Color="{DynamicResource ListTextSelectedColor}"/>
<SolidColorBrush x:Key="{x:Static SystemColors.ControlTextBrushKey}" Color="{DynamicResource ListTextSelectedColor}"/>
</Style.Resources>
</Style>
</ListBox>
APPLYING RESOURCES IN C#
this.Resources["ListSelectedColor"] = SETING.ListSelectedColor.Color;
this.Resources["ListTextSelectedColor"] = SETTING.ListSelectedTextColor.Color;
Solution:
<Window x:Class="ListBoxStyle.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:src="clr-namespace:ListBoxStyle"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<Style x:Key="_ListBoxItemStyle" TargetType="ListBoxItem">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ListBoxItem">
<Border Name="_Border"
Padding="2"
SnapsToDevicePixels="true">
<ContentPresenter />
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsSelected" Value="true">
<Setter TargetName="_Border" Property="Background" Value="Yellow"/>
<Setter Property="Foreground" Value="Red"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
<Grid>
<ListBox ItemContainerStyle="{DynamicResource _ListBoxItemStyle}"
Width="200" Height="250"
ScrollViewer.VerticalScrollBarVisibility="Auto"
ScrollViewer.HorizontalScrollBarVisibility="Auto">
<ListBoxItem>Hello</ListBoxItem>
<ListBoxItem>Hi</ListBoxItem>
</ListBox>
</Grid>
</Window>
Thanks to Vinkal and failedprogramming, I got everything working beautifully. I created the following Resources:
<UserControl.Resources>
<SolidColorBrush x:Key="ListTextSelectedColor" x:Shared="False"/>
<SolidColorBrush x:Key="ListSelectedColor" x:Shared="False"/>
<Style x:Key="_ListBoxItemStyle" TargetType="ListBoxItem">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ListBoxItem">
<Border Name="_Border"
Padding="2"
SnapsToDevicePixels="true">
<ContentPresenter />
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsSelected" Value="true">
<Setter TargetName="_Border" Property="Background" Value="{DynamicResource ResourceKey=ListSelectedColor}"/>
<Setter Property="Foreground" Value="{DynamicResource ResourceKey=ListTextSelectedColor}"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</UserControl.Resources>
And then applied the style to my listbox with:
ItemContainerStyle="{DynamicResource ResourceKey=_ListBoxItemStyle}"
And finally, change the solidcolorbrush resources (therefore changing the setter values) in my C# code by doing the following:
this.Resources["ListSelectedColor"] = EmulatorPage.ListSelectedColor;
this.Resources["ListTextSelectedColor"] = EmulatorPage.ListSelectedTextColor;
Thank you to both of you!
To all the neigh-sayers out there... do not lose hope! it can be done!
I started off with VSS right-click on the listbox and used every "Edit Template" and "Edit Additional Templates" for each thing available until I found the how these things work.
You start off quite simply with a list box, bound to MVVM as normal.
<ListBox Width="100"
x:Name="myComboBox" Margin="8"
ItemsSource="{Binding ListBoxListSource}"
SelectedIndex="{Binding ListBox}">
</ListBox>
In UserControl or Window Resources set up a few things....
ListBoxStyle - This styles the main container of the listbox, you can set the borders, margins, padding etc of the main box here. For my example I'm just getting rid of everything to de-style it.
<UserControl.Resources>
<Style x:Key="ListBoxStyle" TargetType="{x:Type ListBox}">
<Setter Property="Background" Value="Transparent"/>
<Setter Property="Foreground" Value="Transparent"/>
<Setter Property="BorderBrush" Value="Transparent"/>
<Setter Property="BorderThickness" Value="0"/>
<Setter Property="Padding" Value="0"/>
<Setter Property="Margin" Value="0"/>
</Style>
</UserControl.Resources>
ItemContainerStyle - This is the bit that people say can't be re-styled - it contains the "windows-selector-blue" bar when an item is selected, but fear not this too can be re-styled (merge this UserControl.Resources section in combination with the above one).
This section is> changing the template of the ItemContainer from whatever it is to a Border, setting a top margin of 3 to pad things out and setting up a style. All we're doing with this style is adding 3px transparent border to the left and right of the item. Then in the Triggers>IsSelected (target of myBorder), changing the border Brush to Red.
<UserControl.Resources>
<Style x:Key="ItemContainerStyle" TargetType="{x:Type ListBoxItem}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ListBoxItem}">
<Border x:Name="myBorder"
Padding="0" Margin="0 3 0 0"
SnapsToDevicePixels="true"
Style="{DynamicResource borderContent}">
<ContentPresenter />
</Border>
<ControlTemplate.Resources>
<Style x:Key="borderContent" TargetType="Border">
<Setter Property="BorderThickness" Value="3 0 3 0"/>
<Setter Property="BorderBrush" Value="Transparent"/>
</Style>
</ControlTemplate.Resources>
<ControlTemplate.Triggers>
<Trigger Property="IsSelected" Value="true">
<Setter TargetName="myBorder" Property="BorderBrush" Value="Red"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</UserControl.Resources>
ListBoxItemDataTemplate - The next step is to make the item container that displays your data. In my example the YourTextBlockStyler has a trigger on the Text>binding and changes the foreground and background colours of the text. Take note that the foreground and background of the Listbox style are set to transparent, so you have to over ride them in your TextBlock style if you want to see anything.
<UserControl.Resources>
<DataTemplate x:Key="ListBoxItemDataTemplate">
<TextBlock Text="{Binding}" Style="{StaticResource YourTextBlockStyler}"/>
</DataTemplate>
</UserControl.Resources>
Back to the listbox - Now we've set up all the styles and templates in the Resources section we can update the listbox with Style="" ItemContainerStyle="" and ItemTemplate=""
<ListBox Width="100"
x:Name="myComboBox" Margin="8"
ItemsSource="{Binding ListBoxListSource}"
SelectedIndex="{Binding ListBox}"
Style="{StaticResource ListBoxStyle}"
ItemContainerStyle="{StaticResource ItemContainerStyle}"
ItemTemplate="{StaticResource ListBoxItemDataTemplate}">
</ListBox>
Then your boring list box will transform magically in to a totally restyled list box with red border selector
From into
All without editing a single System.ResourceBrush =]

How to modify WPF menu drop down visual?

I have a WPF menu:
XAML:
<Style x:Key="{x:Type ContextMenu}" TargetType="{x:Type ContextMenu}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ContextMenu}">
<Border Background="#FF171616" CornerRadius="5" BorderBrush="DarkGray" BorderThickness="5" Opacity="0.0">
<StackPanel ClipToBounds="True" Orientation="Vertical" IsItemsHost="True" Margin="5,4,5,4"/>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Am I using the correct x:Type ContextMenu to alter the drop down visualization? The menu item visual is altered because I've manually changed the style. But it is the context drop down on which I want to apply visuals.
How can I modify the context drop down itself?
Here is a paint sample of what I'm after:
To modify style for menu, you need to override Menu style.
Like below:
<Style TargetType="{x:Type Menu}">
<Setter Property="BorderThickness" Value="0" />
<Setter Property="Foreground" Value="White" />
<Setter Property="Background" Value="#FF171616" />
</Style>
Edit: you can actually modify the whole template also, if you like. But I think these properties should get you the visualization you are looking for.

Binding content of contentPresenter in UserControl?

I've got a UserControl which is a modified toggleButton.
I've added two String properties to it so I can change (or bind) them in blend, which I want to be the text displayed when the button is toggled. ie when checked, one string is displayed, when unchecked - the other.
Setting the text is fine, and toggling the UserControl is fine, but I don't know how to set the content of the contentpresenter from a property of the toggle by a trigger. Here's a rough look at the code:
<UserControl
x:Name="UserControl"
<UserControl.Resources>
<Style x:Key="BiTextToggleButtonWithBorder" TargetType="{x:Type ToggleButton}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ToggleButton}">
<Grid>
<Path x:Name="path"
Data="M28,0.5 L28.071953,0.50129622 28.092436,0.5 115.90756,0.5 C117.89162,0.50000113 119.5,2.5147196 119.5,5.0000013 L119.61492,36.460156 119.61432,36.857203 C117.1338,37.367692 108.82679,39.239366 106.37993,47.492391 L44.667,47.5 28.092436,47.5 4.9999995,47.5 C2.5147185,47.5 0.5,45.485283 0.5,43 L0.5,21 0.51801485,20.64324 0.5,20.0835 C0.5,9.2678322 12.812169,0.50000072 28,0.5 z"
Stretch="Fill"
<ContentPresenter
x:Name="contentPresenter"
Content="{Binding}"/>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsChecked" Value="True">
<Setter Property="Content" TargetName="contentPresenter" Value="{Binding whatgoeshere!?}"/>
</Trigger>
<Trigger Property="IsChecked" Value="False">
<Setter Property="Content" TargetName="contentPresenter" Value="whatgoeshere!?"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</UserControl.Resources>
<Grid x:Name="LayoutRoot">
<ToggleButton
x:Name="ToggleButton"
Style="{DynamicResource BiTextToggleButtonWithBorder}"
FontSize="18.667"
Foreground="{DynamicResource WhiteText}"/>
</Grid>
</UserControl>
I can't even find the property I want to change. I've done similar bindings to objects within a UserControl before, but nothing that is in a style and in a controlpresenter.
What am I missing?
have you tried changing the {Binding} to a {TemplateBinding} ? (MSDN documentation here)

Categories