Specify a binding in an application-level style resource? - wpf

I'm fairly new to WPF and have created a style to alter the appearance of a button control. The style contains a data trigger to change the button background (amongst other things) based on a boolean property in the data context, e.g.:-
<Style x:Key="IndicatorButton" TargetType="Button">
<DataTrigger Binding="{Binding Path=ValveIsOpen}" Value="True">
<Setter Property="Background" Value="#00FF00"/>
..etc..
Currently the style is only used by a single button, so the data trigger binding is hard-coded with a property called "ValveIsOpen".
I now want to re-use this style throughout my app, with different buttons being bound to different properties. How would I change the data trigger binding on each button that the style is applied to?
Many thanks

You need to define a base style and derived styles, such as
<Style x:Key="IndicatorButton" TargetType="Button">
<Setter Property="Foreground" .../>
...
<Style x:Key="ValveIndicatorButton" TargetType="Button" BasedOn={StaticResource IndicatorButton}>
<DataTrigger Binding="{Binding Path=ValveIsOpen}" Value="True">
<Setter Property="Background" Value="#00FF00"/>
..etc..

Related

XAML Style Trigger - Change Style ONLY for Object of with a specific Name

I am new XAML however I am given the task to override some styles for certain elements within an existing application.
In my custom Theme, I am attempting to override the style of a BORDER control.
From what I can tell (using Snoop) to inspect the application, the element I want to change is just a plain border.
The border also seems to have a Name of "SubMenuBorder". Please see the image below.
Here is the latest iteration of my style snippet in which I am trying to set the border control's Background, BorderBrush and BorderThickness BUT ONLY if the control has a name of "SubMenuBorder"
<Style TargetType="{x:Type Border}">
<Style.Triggers>
<Trigger Property="Name" Value="SubMenuBorder">
<Setter Property="Background" Value="Red"></Setter>
<Setter Property="BorderBrush" Value="Red"></Setter>
<Setter Property="BorderThickness" Value="20"></Setter>
</Trigger>
</Style.Triggers>
</Style>
Unfortunately the above does NOT work.
The style trigger does not seem to fire/apply to the intended control.
If I simplify things further and just style ALL borders with the following snippet, then it seems to work and the border control I want to change, is styled, but so is every other border control in the application.
<Style TargetType="{x:Type Border}">
<Setter Property="Background" Value="Red"></Setter>
<Setter Property="BorderBrush" Value="Red"></Setter>
<Setter Property="BorderThickness" Value="20"></Setter>
</Style>
Further Findings
I attempted to use a DataTrigger... which unfortunately doesn't work either.
Snoop shows below that the data trigger is being satisfied, however on the second image below you can see that the property of the background and borderbrush are still from the parenttemplate.
Any ideas please?
<Style TargetType="{x:Type Border}">
<Style.Triggers>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource Self}, Path=Name}" Value="SubMenuBorder">
<Setter Property="Background" Value="Red"></Setter>
<Setter Property="BorderBrush" Value="Red"></Setter>
<Setter Property="BorderThickness" Value="20"></Setter>
</DataTrigger>
</Style.Triggers>
</Style>
You cannot use triggers to modify a Border that is defined in a ControlTemplate, with the exception of using an implicit Style that applies to all elements of the type specified by the TargetType property of the implicit Style.
You will either have to modify the ControlTemplate itself, or programmatically find the Border element in the visual tree and then change its runtime property values. The first approach, i.e. modifying or creating a custom template, is the recommended approach.
The name "SubMenuBorder" is only known and applicable within that Border element's namescope.

How to modify legacy named style for having different setters based on targetTypes?

I have this named style
<Style x:Key="validationSupport" TargetType="{x:Type Control}">
<Setter Property="Margin" Value="5,2,14,2" />
...OMISSIS...
<Style.Triggers>
...OMISSIS...
<DataTrigger Binding="{Binding DataContext.ActiveWorkspace.Editable, RelativeSource={RelativeSource AncestorType=Window}}" Value="False">
<Setter Property="IsEnabled" Value="False" />
</DataTrigger>
</Style.Triggers>
</Style>
I use it extensively for TextBoxes, ComboBoxes, DatePickers etc, so I used as TargetType a super class for all these elements, Control.
Now I would like to differentiate the setter inside the dataTrigger using specific properties that 'Control' doesn't have. It seems I have to create different styles with different names,each for every targetType I want to differentiate, but that way I have to change the style name inside all elements which use it. Is there a smarter way to achieve that goal ? I don't want want to go and modify every xaml file I have.
Update after first answer
I have tried to put the following setters inside the datatrigger:
<Setter Property="Background" Value="#FFECECF8" />
<Setter Property="CheckBox.IsEnabled" Value="False" />
<Setter Property="DatePicker.IsEnabled" Value="False" />
<Setter Property="ComboBox.IsEnabled" Value="False" />
<Setter Property="TextBox.IsReadOnly" Value="True" />
Unfortunately the tests gave odd results. The IsEnabled property is set for TextBoxes too despite the prefix should limit its application to CheckBoxes, DatePickers and ComboBoxes.
My final need was to make some control contents unchangeable avoiding the difficult to read colors associated with disabled controls. From previous researches I understood that changing the colors for a 'disabled' control is not an easy task and involves the redefinition of the control template. So I thought to apply a combination of IsReadOnly and Background, but it is not applicable for the above problem. In fact CheckBoxes, DatePickers and ComboBoxes can only be made unchangeable using the IsEnabled property.
Am I missing something ?
There is a way, but I have to warn you - this is far from best-practice and should be avoided
WPF allows you to use desired type as a prefix for the property. That way, if you apply the style to a control that doesn't inherit from the prefixed type - the setter is ignored.
<Style x:Key="validationSupport" TargetType="{x:Type Control}">
<Setter Property="Margin" Value="5,2,14,2" />
...OMISSIS...
<Style.Triggers>
...OMISSIS...
<DataTrigger Binding="{Binding DataContext.ActiveWorkspace.Editable, RelativeSource={RelativeSource AncestorType=Window}}" Value="False">
<Setter Property="IsEnabled" Value="False" />
<Setter Property="Button.Background" Value="Red" />
</DataTrigger>
</Style.Triggers>
</Style>
[Test this extensively, since I suspect that it might create memory leaks.]

Style a custom property of a custom control

In an application that I am working I need to have all of my next buttons look the same. So my plan was to make a style called next button style and apply as needed. However I need to set some dependency properties on the button to be the same.
Here is the xmal for the style
<Style x:Key="NextButtonStyle" TargetType="diControls:DIButton">
<Setter Property="Margin" Value="5"></Setter>
<Setter Property="DIButtonStyle" Value="DIGreenImageButtonWithText_Medium"></Setter>
<Setter Property="Image" Value="RightArrowIcon_Gray"></Setter>
<Setter Property="HoverImage" Value="RightArrowIcon_White"></Setter>
<Setter Property="Command" Value="{Binding Path=NextPressedCommand}"></Setter>
</Style>
The custom properties DIButtonStyle,Image and HoverImage all say an exception has been thrown by the target of an invocation. Is there any way around this error?
Thanks!

Different template on different ListBoxItem value without DataTemplateSelector?

As title, is it possible?
I have seen in TreeView you can defines different HierarchicalDataTemplate for different datatype using DataType attribute, it doens't even need DataTemplateSelector.
So I wonder if is possible to choose a template according to a binded value without using DataTemplateSelector?
In my condition, is very simple, if the data object's Property = 1, then display template1, 2 then template2.
Is it possible to do it without DataTemplateSelector?
Yes, you can use a DataTrigger
<Style TargetType="{x:Type ListBoxItem}">
<Setter Property="Template" Value="{StaticResource DefaultTemplate}" />
<Style.Triggers>
<DataTrigger Binding="{Binding SomeProperty}" Value="2">
<Setter Property="Template" Value="{StaticResource Template2}" />
</DataTrigger>
</Style.Triggers>
</Style>
I actually prefer DataTriggers to a DataTemplateSelector because they respond to PropertyChange notifications, and I prefer to see my UI logic in my UI code.

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

Resources