ControlTemplate set foreground of ContentPresenter containing UserControl with trigger - c#

I am creating a ControlTemplate for a TabItem and I am trying to set the foreground color of the usercontrol contained in the ContentPresenter used in the template. I can not set it on the ContentPresenter itself because a ContentPresenter does not have the Foreground Property.
The reason why I want to set it on the ContentPresenter (or the user control in the ContentPresenter), and not on the TabItem itself is because changing the TabItem Foreground color causes the object in the TabItem's Content Property to also inherit the foreground color, and not just the header, which is not intended. Basically I only wan't to change the TabItems Header object Foreground color, which is represented by the Content Presenter.
This is what my code looks like:
<Style TargetType="TabItem"
BasedOn="{StaticResource {x:Type TabItem}}">
<Setter Property="Background"
Value="Transparent" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="TabItem">
<Grid x:Name="Root">
<ContentPresenter x:Name="Presenter" HorizontalAlignment="Center"
VerticalAlignment="Center"
ContentSource="Header"
Margin="10 20" />
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver"
Value="True">
<Setter Property="Panel.ZIndex"
Value="100" />
<Setter TargetName="Presenter"
Property="UserControl.Foreground"
Value="#90E53935" />
</Trigger>
<Trigger Property="IsSelected"
Value="True">
<Setter Property="Panel.ZIndex"
Value="100" />
<Setter TargetName="Presenter"
Property="UserControl.Foreground"
Value="#E53935" />
<Setter TargetName="Root"
Property="Background"
Value="{StaticResource TransparentWheat}" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
I already tried to substitute UserControl for the exact name of the user control, which also appears to not work.

Related

Cannot Change Hovering Color of `ToggleButton`

I am trying to change the hovering color of a toggle button when the button is checked. For some reason, it doesn't work. However, I can change the thickness of the border.
<!--App.xaml-->
<ResourceDictionary>
<Style x:Key="MyStyle" TargetType="{x:Type ToggleButton}">
<Style.Triggers>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="IsChecked" Value="True"/>
<Condition Property="IsMouseOver" Value="True"/>
</MultiTrigger.Conditions>
<Setter Property="BorderThickness" Value="10"/> <!--works-->
<Setter Property="BorderBrush" Value="Red"/> <!--fails-->
<Setter Property="Background" Value="Red"/> <!--fails-->
</MultiTrigger>
</Style.Triggers>
</Style>
</ResourceDictionary>
<!--MainWindow.xaml-->
<Grid>
<ToggleButton
Style="{StaticResource MyStyle}"
Content="Button"
HorizontalAlignment="Left"
Margin="271,0,0,0"
VerticalAlignment="Center"
Height="108"
Width="192"
/>
</Grid>
That's because ToggleButton has a ControlTemplate that defines its visual structure - and this template has precedence over style setters.
The template uses a Border element with x:Name Border to render the ToggleButton's border, and a Grid named ContentPresenter to host the button's content.
The template also defines triggers that modify the Border's properties when the button is in the MouseOver state - and these have a higher precedence than style triggers.
To override this behavior, you need to use template bindings in your style:
<Style x:Key="MyStyle" TargetType="ToggleButton">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ToggleButton">
<Border x:Name="Border"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}">
...
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="IsChecked" Value="True"/>
<Condition Property="IsMouseOver" Value="True"/>
</MultiTrigger.Conditions>
<Setter Property="BorderBrush" Value="Red"/>
<Setter Property="BorderThickness" Value="10"/>
</MultiTrigger>
</Style.Triggers>
</Style>
Now the style setters will update the properties of the Border element defined in the template, overriding the default hover behavior.
Note that you should also include template bindings for all the other properties you want to be styled (Background, Foreground etc.).

How to implement event triggered WPF control style change

I have an style template for my Button control that looks like that:
<Style x:Key="myBtnStyle" TargetType="{x:Type Button}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Border x:Name="border" Width="100" Height="25"
Padding="5,5,5,5" CornerRadius="5,5,5,5"
Background="LightGray" BorderBrush="Black"
BorderThickness="1,1,1,1">
<ContentPresenter
x:Name="cpButton"
VerticalAlignment="Center"
HorizontalAlignment="Center"
Width="Auto" Height="Auto" Margin="-6">
</ContentPresenter>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsEnabled" Value="false">
<Setter Property="Background" TargetName="border" Value="GhostWhite"></Setter>
<Setter Property="BorderBrush" TargetName="border" Value="Gainsboro"></Setter>
<Setter Property="Foreground" Value="Gray"></Setter>
</Trigger>
<Trigger Property="IsPressed" Value="true">
<Setter Property="Background" TargetName="border" Value="SkyBlue"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
My button instance itself:
<Button Name="btnResetCount" Content="Reset" Command="{Binding Path=CalcViewModel.ResetCounter}" Style="{StaticResource myBtnStyle}" IsEnabled="{Binding IsButtonCounterEnabled,Mode=OneWay, UpdateSourceTrigger=PropertyChanged}">
The binding for the IsEnabled Property works fine. So the IsEnabled gets set correct from my ViewModel.
But my problem is that my Styles for IsEnabled <ControlTemplate.Triggers> do not get loaded/refreshed automatically in the UI. I always have to click to any other control first. Once i clicked to another control the style of the button changes also.
What can i do to automatically update the/refresh the style of the button/any control based on the IsEnabled event?
EDIT:
Finally i got it working with an workaround. Im not happy and im sure that there are better ways. But it works.
In my ViewModel i implemented:
CommandManager.InvalidateRequerySuggested();
By calling this method the controls get refreshed.
I just call it in one of my Properties if the condition is given to enable the button..
Thanks in advance!
Create one more trigger and set the style for IsEnabled=true:
<Trigger Property="IsEnabled" Value="true">
<Setter Property="Background" TargetName="border" Value="Black"/>
<Setter Property="BorderBrush" TargetName="border" Value="Black"/>
<Setter Property="Foreground" Value="Gray"/>
</Trigger>

How to display validation error in button bound to command

I am developing an app and trying to stick to an MVVM model, I have a button that is just bound to a command. Currently the command can be enabled / disabled and greys out the button when the command is set to CanExecute false. But I want to add the ability to show validation errors on the button (by changing its color to red, maybe showing a tooltip).
The validation is currently already shown in another location in the UI, but the users are getting annoyed that the button is enabled and they click it only to have a message box pops up telling them they shouldn't of clicked the button because there is a validation error. But just disabling the button completely without showing them why would make the users confused(since they overlook the large red validation error at the bottom of the screen). So how can I show additional information on the save button?
This is my current button xaml:
<Button Name="SaveDataPointButton" Style="{StaticResource ImageButton}" Command="{Binding OpenDataPoint.SaveDataPointEditsCommand}" Margin="5,0" VerticalAlignment="Stretch">
<Image Source="save.png" Stretch="None" ToolTip="Save Edits"/>
</Button>
And the style that is changing its appearance when IsEnabled is false, is it possible to somehow inspect some other command state from the style to make the button red during validation errors?
<!-- Style for all of the nav and save buttons in the datapoints view -->
<Style x:Key="ImageButton" TargetType="Button">
<Setter Property="SnapsToDevicePixels" Value="true"/>
<Setter Property="OverridesDefaultStyle" Value="true"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Border
x:Name="Border"
BorderThickness="0">
<ContentPresenter
Margin="2"
HorizontalAlignment="Center"
VerticalAlignment="Center"
RecognizesAccessKey="True"/>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="true">
<Setter TargetName="Border" Property="Background">
<Setter.Value>
<SolidColorBrush Color="LightGray"/>
</Setter.Value>
</Setter>
</Trigger>
<Trigger Property="IsPressed" Value="true">
<Setter TargetName="Border" Property="Background">
<Setter.Value>
<SolidColorBrush Color="Gray"/>
</Setter.Value>
</Setter>
</Trigger>
<Trigger Property="IsEnabled" Value="false">
<Setter Property="Opacity" Value="0.3"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="Command" Value="{x:Null}">
<Setter Property="IsEnabled" Value="False"/>
</Trigger>
</Style.Triggers>
</Style>
I ended up using a DataTrigger bound to another property on the view model to change the background color, but because I was setting a ControlTemplate in the style, the only way I could figure out to make the DataTrigger apply a background change was to copy the entire style and put the DataTrigger on the Border element in the control template. Since apparently you can't reference a child element from the new ControlTemplate from outside that control template.
<!-- Style for all of the nav and save buttons in the datapoints view -->
<Style x:Key="ImageSaveButton" TargetType="Button">
<Setter Property="SnapsToDevicePixels" Value="true"/>
<Setter Property="OverridesDefaultStyle" Value="true"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Border
x:Name="Border"
BorderThickness="0">
<Border.Style>
<Style TargetType="Border">
<Style.Triggers>
<DataTrigger Binding="{Binding OpenDataPoint.HasValidationError}" Value="True">
<Setter Property="Background" Value="Red"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Border.Style>
<ContentPresenter
Margin="2"
HorizontalAlignment="Center"
VerticalAlignment="Center"
RecognizesAccessKey="True"/>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="true">
<Setter TargetName="Border" Property="Background">
<Setter.Value>
<SolidColorBrush Color="LightGray"/>
</Setter.Value>
</Setter>
</Trigger>
<Trigger Property="IsPressed" Value="true">
<Setter TargetName="Border" Property="Background">
<Setter.Value>
<SolidColorBrush Color="Gray"/>
</Setter.Value>
</Setter>
</Trigger>
<Trigger Property="IsEnabled" Value="false">
<Setter Property="Opacity" Value="0.3"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="Command" Value="{x:Null}">
<Setter Property="IsEnabled" Value="False"/>
</Trigger>
</Style.Triggers>
</Style>

Styling of ListBoxItem Not Working

I'm attempting to style the ListBoxItems within my listbox but the content of the ListBoxItem does not display and any change in colours are not apparent. The only thing working is the "border bottom" that I've applied to the bottom of each list item.
<Style x:Key="ListItemStyle" TargetType="{x:Type ListBoxItem}">
<Setter Property="OverridesDefaultStyle" Value="True" />
<Setter Property="MinHeight" Value="30" />
<Setter Property="Foreground" Value="Black" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Border BorderBrush="#FF66AFDE" BorderThickness="0 0 0 1" />
<ControlTemplate.Triggers>
<Trigger Property="IsFocused" Value="True">
<Setter Property="Background" Value="Red"></Setter>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Use a panel/container/decorator in the ListBoxItem control template to set the background color. (It seems that the logic for setting the selection background color will interfere with attempts to control its background color.)
<ControlTemplate TargetType="{x:Type ListBoxItem}">
<Border
Name="PART_Border"
Focusable="true"
Background="{TemplateBinding Background}"
BorderBrush="#FF66AFDE"
BorderThickness="0 0 0 1"
>
<ContentPresenter />
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsFocused" Value="True">
<Setter
Property="Background"
Value="Red"
TargetName="PART_Border"
/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
Also note, that Border.Focusable is by default false. If setting it to true doesn't work (i admit, i haven't tested), use a different container control instead of Border.
Also, if the content you will show has any controls receiving focus (such as buttons or text fields), the trigger might not work as expected, because the Border might not have the focus when a control of the content has the focus. Also, tabbing from control to control might exhibit unexpected behaviour. If you have to deal with such a situation, try to handle the trigger in an ItemTemplate instead.
Regarding the ContentPresenter not showing anything: Depending on the type of the elements in the ItemsSource, you might need to define a ListBox.ItemTemplate (or ListBox.ItemTemplateSelector), otherwise ContentPresenter might not know what to display.
Try this
<Style x:Key="ListItemStyle" TargetType="{x:Type ListBoxItem}">
<Setter Property="OverridesDefaultStyle" Value="True" />
<Setter Property="MinHeight" Value="30" />
<Setter Property="Foreground" Value="Black" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ListBoxItem}">
<Border BorderBrush="#FF66AFDE" BorderThickness="0 0 0 1" x:Name="border">
<ContentPresenter/>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsFocused" Value="True">
<Setter TargetName="border" Property="Background" Value="Red"></Setter>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
I hope this will help

How to change the color of the selected tab in the TabControl?

I am implementing a TabControl for a dialog box in WPF. The color of the selected tab (mouse-down) is white by default. I want to change the color of that selected tab to the color of hover (when I hover over a tab, the color of the tab changes to an Office-blue-gradient, which is what I want the color of the selected tab to be on mouse-click).
How can I do that?
This piece of code does not work:
<Style x:Key="StyleTabControl" TargetType="{x:Type TabItem}">
<Setter Property="Background" Value="#FFFDFDFD"/>
<Style.Triggers>
<Trigger Property="IsSelected " Value="True">
<Setter Property="Background" Value="SlateGray"></Setter>
</Trigger>
</Style.Triggers>
</Style>
Note: I also tried IsMouseCaptured event for the trigger property. Still does not work.
Alright...after hours of trying, I have realized that the TabItem selection behaviour is defined at the Template level. So, if I wana change the backgrnd color, I do this:
<Window.Resources>
<Style TargetType="{x:Type TabItem}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type TabItem}">
<Border>
<Grid>
<Grid>
<Border x:Name="border"
CornerRadius="3,3,0,0"
Background="WhiteSmoke"/>
</Grid>
<ContentPresenter ContentSource="Header"
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}" />
</Grid>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver"
Value="True">
<Setter TargetName="border"
Property="Background"
Value="LightGray" />
</Trigger>
<Trigger Property="IsSelected"
Value="True">
<Setter TargetName="border"
Property="Background"
Value="LightGray" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>

Categories