Different template on different ListBoxItem value without DataTemplateSelector? - wpf

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.

Related

Why WPF Triggers must be declared into a style (even in-line)?

I don't understand why WPF allows me to write both
<Grid>
<Grid.Triggers>
<DataTrigger Binding="{Binding HasNeverBeenSeen}" Value="true">
<Setter Property="Background" Value="Red"/>
</DataTrigger>
</Grid.Triggers>
</Grid>
and
<Grid>
<Grid.Style>
<Style TargetType="{x:Type Grid}">
<Style.Triggers>
<DataTrigger Binding="{Binding HasNeverBeenSeen}" Value="true">
<Setter Property="Background" Value="Red"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Grid.Style>
</Grid>
but only the second seems to work. Why is there a Triggers tag to Grid element if we must use a Style?
Thanks
Short answer to your question is because this is how it is designed by WPF team.
FrameworkElement.Triggers can only have EventTriggers although property is collection of TriggerBase. It's also clearly stated on MSDN page:
Note that the collection of triggers established on an element only
supports EventTrigger, not property triggers (Trigger). If you require
property triggers, you must place these within a style or template and
then assign that style or template to the element either directly
through the Style property, or indirectly through an implicit style
reference.

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.]

Specify a binding in an application-level style resource?

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..

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

How do I trigger a style change if DataContext is null or not using WPF

I have a page with several controls. The controls are bound to display values which they get from the page's DataContext. What I would like to do is display another look of the page should the DataContext be null. In some cases the controls of the page should display differently if "their" property is set or not.
Is is possible to create a binding to see if the DataContext is set?
What I did as a workaround was to add a IsDataContextSet property to the page and the specify a binding like:
Binding="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Page}}, Path=IsDataContextSet}" Value="false"
This works as I expect but I have a feeling that their is more elegant way to do this. Or at least or more WPFish way.
Given the scenario you describe, I would set the properties with a style and a data trigger. The data trigger would use the default binding which is the data context.
An example might look like this:
<Border>
<Border.Style>
<Style TargetType="Border">
<Setter Property="Background"
Value="Orange" />
<Style.Triggers>
<DataTrigger Binding="{Binding}"
Value="{x:Null}">
<Setter Property="Background"
Value="Yellow" />
</DataTrigger>
</Style.Triggers>
</Style>
</Border.Style>
</Border>
The border will be orange unless the data context is null, in which case the background is yellow.

Resources