Conditional BasedOn in XAML styles - wpf

Given:
<Style x:Key="ThirdLevelGroupBoxStyle" TargetType="GroupBox" BasedOn="{StaticResource MetroGroupBox}">
<Setter Property="Background" Value="{DynamicResource AccentColorBrush3}" />
</Style>
<Style x:Key="SecondLevelGroupBoxStyle" TargetType="GroupBox" BasedOn="{StaticResource MetroGroupBox}">
<Setter Property="Background" Value="{DynamicResource AccentColorBrush2}" />
</Style>
<Style TargetType="GroupBox" x:Key="WidgetControlTemplateStyle" BasedOn="{StaticResource ThirdLevelGroupBoxStyle}">
<Style.Triggers>
<DataTrigger Binding="{Binding CanExecuteClickCommand}" Value="True">
<!-- TODO: SecondLevelGroupBoxStyle -->
<Setter Property="Background" Value="{DynamicResource AccentColorBrush2}" />
</DataTrigger>
</Style.Triggers>
</Style>
<ControlTemplate TargetType="ContentControl" x:Key="WidgetControlTemplate">
<GroupBox ... Style="{StaticResource WidgetControlTemplateStyle}">
<ContentPresenter />
</GroupBox>
</ControlTemplate>
ControlTemplate has style WidgetControlTemplate. I want to base (BasedOn) the WidgetControlTemplate style conditionally on ThirdLevelGroupBoxStyle or SecondLevelGroupBoxStyle, in order to avoid XAML duplication. Is there way to achieve this?
If I can't do that, I have to duplicate the definition of SecondLevelGroupBoxStyle.

Is there way to achieve this?
No, not in XAML. The base style to base the style on must be known at design-time.
If I can't do that, I have to duplicate the definition of SecondLevelGroupBoxStyle.
Since you have two different styles that are based on MetroGroupBox, each style that is based on either of this will always be a separate style because you cannot base a single style on more than one style.
What you could do is to use a trigger that sets the Background property of the GroupBox to either AccentColorBrush3 or AccentColorBrush2 instead of trying to inherit this property from another style. It seems like you are already doing this in your WidgetControlTemplateStyle style. And yes, if the ThirdLevelGroupBoxStyle/SecondLevelGroupBoxStyle sets more than one property you will have to set all these in the WidgetControlTemplateStyle as well.
I am afraid there is no way around this unless you merge your two style into one or define them programatically somehow.

Related

XAML separate binding of ContainerStyle and ContentTemplate in ListBox

I tried this for a while now and searched in the web without success... Now I dare to ask on stackoverflow myself.
So, the aim is to separate the definitions of the ItemContainerStyle and the ContentTemplate of a ListBoxItem. I have defined the ListBoxItemStyle in a ResourceDictionary and two different DataTemplates in the Window.Resources.
I now like to set the style of the ListBoxItem according to the one defined in the ResourceDictionary and change the DataTemplate with a trigger (IsMouseOver).
My (not working) Code so fare looks like this:
<ListBox HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Margin="20,60,10,10"
Grid.Row="1" Grid.Column="0"
PreviewMouseMove="DragMove_PreviewMouseMove"
PreviewMouseLeftButtonDown="Drag_PreviewMouseLeftButtonDown"
ItemsSource="{Binding Persons}" VerticalContentAlignment="Center"
Style="{StaticResource DefaultListBoxStyle}">
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Setter Property="Style" Value="{StaticResource DefaultListBoxItemStyle}"/>
<Setter Property="ContentTemplate" Value="{StaticResource PersonsListBoxItemTemplate_default}"/>
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="ContentTemplate" Value="{StaticResource PersonsListBoxItemTemplate_infoButtons}"/>
</Trigger>
</Style.Triggers>
</Style>
</ListBox.ItemContainerStyle>
</ListBox>
where DefaultListBoxStyle is the style defined in the ResourceDictionary and PersonsListBoxItemTemplate_default & PersonsListBoxItemTemplate_infoButtons are the DataTemplates defined in the Window.Resources.
Now I get an error, that the style-object must not have an impact on the style-property of the object it belongs to.
I tried different ways, but without success... I also tried to define the style first and then change the template, but it does not work either.
Thank you all for your help!
So you can't set Style Property in Style with Setter. For this you need to use BasedOn:
<Style TargetType="ListBoxItem" BasedOn="{StaticResource DefaultListBoxItemStyle}">
<Setter ... />
</Style>

Use trigger to change colour of a path declared in ResourceDictionary

I'm creating a radiobutton style. The RadioButton has a Border which hosts a ContentControl. The ContentControl has its Content property set to a Path (FemaleVector) declared in a separate ResourceDictionary. How can I change the Fill property of the path when the radiobutton IsChecked? Below is what I have so far. I am able to change the background property of the border but setting the Foreground property of the ContentControl does not change the colour of the path. (Didn't think that would work.)
<Style x:Key="Female" TargetType="{x:Type RadioButton}">
<Setter Property="Margin" Value="0,0,5,0"/>
<Setter Property="Cursor" Value="Hand"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type RadioButton}">
<Border x:Name="border" Padding="7,3,7,3" Width="35" Height="35" BorderBrush="#8CD567DC" Background="#00D567DC" CornerRadius="5" BorderThickness="0.8">
<ContentControl x:Name="content" Content="{DynamicResource FemaleVector}"/>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsChecked" Value="True">
<Setter Property="Background" TargetName="border" Value="#8CD567DC"/>
<Setter Property="Foreground" TargetName="content" Value="Blue"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
I don't like having the long Data property of paths in my Styles, so I have moved them into a separate ResourceDictionary. Should I just put the Path back into my Style instead of keeping it in a separate ResourceDictionary?
Edit: similar questions are here and here.
If the style is not reused somewhere, I would personally keep it in local style resources section. That way you see bigger picture. Otherwise it would be wise to keep it in ResourceDictionary :)
Either way you should be able to change Fill property with:
<Setter Property="Content.Fill" TargetName="content" Value="Blue"/>
If this is not working, I advise few ways more:
You can use Trigger.EnterActions<> in Xaml. Perhaps setting property through animation will have better effect? ControlTemplate triggers with setters are sometimes way limiting.
There's also relative binding. But you gotta be careful with that. (If you pla to make it reusable)
In your FemaleVector style, you can bind Fill against ContentControl Foreground. Look for RelativeBinding in Google.
And then there's property inheritance. If you set Fill color in FemaleVector, you need to do it with style. Such as:
<Style>
<Setter Property="Fill" Value="BLACK" />
</Style>
you can later set ContentControls Style and add trigger there, like:
<ContentControl.Style>
<Style>
<Style.Triggers>
<DataTrigger Binding={Binding IsChecked, RelativeSource={RelativeSource AncestorType=RadioButto}} Value=TRUE>
<Setter Property="Path.Fill" Value="BLACK" />
</DataTrigger>

Style Trigger to Apply another Style

I am sure this has been asked before, but I haven't had an easy time figuring out how to phrase the query.
I have this style;
<SolidColorBrush x:Key="SemiTransparentRedBrushKey">#F0FF0000</SolidColorBrush>
<Style x:Key="TextBoxEmptyError" TargetType="{x:Type TextBox}">
<Style.Triggers>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource Self}, Path=Text.Length}" Value="0">
<Setter Property="BorderBrush" Value="{StaticResource ResourceKey=SemiTransparentRedBrushKey}"/>
</DataTrigger>
</Style.Triggers>
</Style>
That I can apply to Textboxes to have a red border when they are empty. Its great, I can just add Style="{StaticResource TextBoxEmptyError}" to the Control Tag. But what if I want to apply this style with a trigger, so that the control only used it under certain conditions (like a binding being true)? Something like:
<TextBox.Style>
<Style TargetType="TextBox">
<Style.Triggers>
<DataTrigger Binding="{Binding Path=ApprovedRequired}" Value="True">
<Setter Property="Style" Value="{StaticResource TextBoxEmptyError}"></Setter>
</DataTrigger>
</Style.Triggers>
</Style>
This code throws an exception though {"Style object is not allowed to affect the Style property of the object to which it applies."}
Can something like this be done?
Edit: If this cannot be done with a Style trigger because it would overwrite itself, is there another way to Conditionally apply a resource style?
Edit: I can change the question title if there is a more proper term for this action.
Styles cannot be set from a Setter within the Style, because then essentially the first Style would never exist at all.
Since you're looking for a Validation style, I would recommend looking into Validation.ErrorTemplate, although if that doesn't work you can change your trigger so it modifies specific properties such as BorderBrush instead of the Style property
i would think of using a Template with a TemplateTrigger and there you can change the style to what ever you like based on what ever condition

Apply default WPF style

I have a global style that sets all my TextBox styles, but in some cases I want to revert just the Foreground color to the original non-custom-style color. I tried using {TemplateBinding Foreground} inside the specific TextBoxes that I wanted to revert. It didn't end up being valid XAML and I'm not sure that's the right way anyhow.
Any ideas? Thanks.
There's a few ways this could be done. If you look at the Precedence List on the MSDN
then you can see that the Forground set in ways 1-8 will override the Foreground from a default style. The easiest way being just to set the local value in the TextBox.
<TextBox Foreground="Red" />
Another thing that you can do is use the 'BasedOn' property of styles to override the other versions. This does require giving a key value to your default style, but that can then be used to also apply the default like in this example:
<Style TargetType="{x:Type TextBox}"
x:Key="myTextBoxStyle">
<Setter Property="Foreground"
Value="Red" />
<Setter Property="FontWeight"
Value="Bold" />
</Style>
<!-- Style applies to all TextBoxes -->
<Style TargetType="{x:Type TextBox}"
BasedOn="{StaticResource myTextBoxStyle}" />
<TextBox Text="Hello">
<TextBox.Style>
<Style BasedOn="{StaticResource myTextBoxStyle}" TargetType="{x:Type TextBox}">
<Setter Property="Foreground"
Value="Blue" />
</Style>
</TextBox.Style>
</TextBox>
Edit:
In the case that the default style is applying a value and you want to revert it to the base value there are a few ways I can think of, off hand, to get this behavior. You can't, that I know of, bind back to the default theme value in a generic manner.
We can however do some other things. If we need the style to not apply some properties, we can set the style to {x:Null}, thus stopping the default style from applying. Or we can give the element it's own style that does not inherit from the base style and then re-apply only the setters that we need:
<TextBox Text="Hello" Style="{x:Null}" />
<TextBox Text="Hello">
<TextBox.Style>
<Style TargetType="{x:Type TextBox}">
<Setter Property="FontWeight"
Value="Bold" />
</Style>
</TextBox.Style>
</TextBox>
We could modify the default style so that the Foreground will only be set on certain conditions, such as the Tag being a certain value.
<Style TargetType="{x:Type TextBox}"
x:Key="myTextBoxStyle">
<Setter Property="FontWeight"
Value="Bold" />
<Style.Triggers>
<Trigger Property="Tag"
Value="ApplyForeground">
<Setter Property="Foreground"
Value="Red" />
</Trigger>
</Style.Triggers>
</Style>
<TextBox Text="Hello" />
<TextBox Text="Hello" Tag="ApplyForeground" />

Can you define multiple TargetTypes for one XAML style?

In HTML/CSS you can define a style which can be applied to many types of elements, e.g.:
.highlight {
color:red;
}
can be applied to both P and DIV, e.g.:
<p class="highlight">this will be highlighted</p>
<div class="highlight">this will also be highlighted</div>
but in XAML you seem to have to define the TargetType for styles, otherwise you get an error:
<Style x:Key="formRowLabel" TargetType="TextBlock">
is there a way to allow a XAML style to be applied to multiple elements or even to leave it open as in CSS?
The setters in WPF styles are checked during compile time; CSS styles are applied dynamically.
You have to specify a type so that WPF can resolve the properties in the setters to the dependency properties of that type.
You can set the target type to base classes that contain the properties you want and then apply that style to derived classes. For example, you could create a style for Control objects and then apply it to multiple types of controls (Button, TextBox, CheckBox, etc)
<Style x:Key="Highlight" TargetType="{x:Type Control}">
<Setter Property="Foreground" Value="Red"/>
</Style>
...
<Button Style="{StaticResource Highlight}" Content="Test"/>
<TextBox Style="{StaticResource Highlight}" Text="Test"/>
<CheckBox Style="{StaticResource Highlight}" Content="Test"/>
<!-- Header text style -->
<Style x:Key="headerTextStyle">
<Setter Property="Label.VerticalAlignment" Value="Center"></Setter>
<Setter Property="Label.FontFamily" Value="Trebuchet MS"></Setter>
<Setter Property="Label.FontWeight" Value="Bold"></Setter>
<Setter Property="Label.FontSize" Value="18"></Setter>
<Setter Property="Label.Foreground" Value="#0066cc"></Setter>
</Style>
<!-- Label style -->
<Style x:Key="labelStyle" TargetType="{x:Type Label}">
<Setter Property="VerticalAlignment" Value="Top" />
<Setter Property="HorizontalAlignment" Value="Left" />
<Setter Property="FontWeight" Value="Bold" />
<Setter Property="Margin" Value="0,0,0,5" />
</Style>
I think both of these methods of declaring a style might answer your question.
In the first one, there is no TargetType specified, but the property names are prefixed with 'Label'. In the second one, the style is created for Label objects.
Another method to do it is:
<UserControl.Resources>
<Style x:Key="commonStyle" TargetType="Control">
<Setter Property="FontSize" Value="24"/>
</Style>
<Style BasedOn="{StaticResource commonStyle}" TargetType="ListBox"/>
<Style BasedOn="{StaticResource commonStyle}" TargetType="ComboBox"/>
</UserControl.Resources>
I wanted to apply a style to a Textblock and a TextBox but the selected answer didn't work for me because Textblock doesn't inherit from Control, in my case I wanted to affect the Visibility property, so I used FrameworkElement
<Style x:Key="ShowIfRequiredStyle" TargetType="{x:Type FrameworkElement}">
<Setter Property="Visibility" Value="Collapsed"/>
<Style.Triggers>
<DataTrigger Binding="{Binding ShowIfRequiredStyle, UpdateSourceTrigger=PropertyChanged}" Value="true">
<Setter Property="Visibility" Value="Visible"/>
</DataTrigger>
</Style.Triggers>
</Style>
<TextBlock Style="{StaticResource ResourceKey=ShowIfRequiredStyle}"/>
<TextBox Style="{StaticResource ResourceKey=ShowIfRequiredStyle}"/>
This works for the Visibility property because both items inherit from Frameworkelement and the property is defined there. Of course this will not work for properties defined only in Control, you can search the hierarchy tree and try to find a base class, anyway I thought this could help someone since this is a top search result and the selected answer is a little incomplete.
There is an alternative answer to the question. You CAN leave the TargetType parameter off the style altogether which will allow it to apply to various different controls, but only if you prefix the property name with "Control."
<Style x:Key="Highlight">
<Setter Property="Control.Foreground" Value="Red"/>
</Style>
Obviously, this only works for properties of the base control class. If you tried to set ItemsSource say, it would fail because there is no Control.ItemsSource
I got this working
<Style x:Key="HeaderStyleThin" TargetType="{x:Type Border}">
<Setter Property="Background" Value="Black" />
<Style.Resources>
<Style TargetType="{x:Type TextBlock}">
<Setter Property="Background=" Value="Red" />
</Style>
</Style.Resources>
</Style>

Resources