One DataTrigger for multiple properties - wpf

I have a listview bound to an Observable collection, the listview has properties that most of them will use the same trigger.
is it possible to define the trigger once in the resource section and just refer to it once needed by the properties ?
so far i come to this :
<Style TargetType="TextBlock" x:Key="Pstyle">
<Setter Property="Text" Value="Testing"/>
<Style.Triggers>
<DataTrigger Binding="{Binding P1}" Value="Testing">
<Setter Property="Foreground" Value="DarkGreen"/>
<Setter Property="FontWeight" Value="Bold"/>
</DataTrigger>
</Style.Triggers>
</Style>
and in the listview member i just apply the defined style to the propertie P1
Style="{DynamicResource Pstyle}"
but how to apply the same defined trigger for let say P2, P3, P4...

Since you want to put your trigger logic on the content of the TextBlock, in my opinion you should use a Trigger targeting Text property, instead of a DataTrigger.
Check out this sample code:
<Style TargetType="TextBlock" x:Key="Pstyle">
<Style.Triggers>
<Trigger Property="Text" Value="Testing">
<Setter Property="Foreground" Value="DarkGreen"/>
<Setter Property="FontWeight" Value="Bold"/>
</Trigger>
</Style.Triggers>
</Style>
<TextBlock Name="MyTextBlock1" Text="MyTextBlock1Text" Style="{StaticResource Pstyle}"/>
<TextBlock Name="MyTextBlock2" Text="MyTextBlock2Text" Style="{StaticResource Pstyle}"/>
<TextBlock Name="MyTextBlock3" Text="MyTextBlock3Text" Style="{StaticResource Pstyle}"/>
as you can see there is only one single style applied to different Textblocks.
Then you should adapt this to your listview.

Related

Bind DataGridCell style directly to its contents

I have a StatusCell style for DataGridCell that I would like to use in several place in my application. I would like to externalize the Style tag so that I can reuse it easily without having to duplicate the code in my XAML everywhere.
Every other source I've found has required me to bind the trigger off the property from my ViewModel. But across the application, the column might be bound to MyStatusProperty or SubObject.MyStatusProperty, etc, so I want to do this to allow me to have one style that will apply to all of these without having to specify where it's binding from.
I am able to do this with a TextBlock with the following style. This lets me bind the TextBlock to whatever I want and the style binding doesn't matter where it's coming from.
<Style x:Key="StatusLabel" TargetType="TextBlock">
<Style.Triggers>
<Trigger Property="Text" Value="Completed">
<Setter Property="Foreground" Value="Green"/>
</Trigger>
</Style.Triggers>
</Style>
and when I create a textblock that I want to use this styling, all I have to do is
<TextBlock Style="{StaticResource StatusLabel}" Text="{Binding Whatever}" />
But with a DataGridCell it doesn't let me do this
<Style x:Key="StatusCell" TargetType="DataGridCell">
<Style.Triggers>
<Trigger Property="Content" Value="Completed">
<Setter Property="Background" Value="Green"/>
<Setter Property="Foreground" Value="White" />
</Trigger>
</Style.Triggers>
</Style>
<DataGridTextColumn Header="Status" Binding="{Binding MyStatusProperty}"
CellStyle="{StaticResource StatusCell}" />
I also tried setting up the trigger like this:
<DataTrigger Binding="{Binding Content, RelativeSource={RelativeSource Self}}" Value="Reviewed">
<Setter Property="Background" Value="Green"/>
<Setter Property="Foreground" Value="White" />
</DataTrigger>
But neither of these work. I have also tried swapping out "Content" in the last example for "Binding" and "Text"
Is there another property I can bind to in the DataGridCell that will let bind the style trigger to the contents of the cell without knowing the binding path?
As usual, I found a workaround shortly after asking. Since it's working with TextBlocks, I just have to use TemplateColumns instead of TextColumns, although I'd still prefer to be able to use TextColumns since they'd use 6 less lines of XAML.
<DataGridTemplateColumn Header="Status">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding MyProperty}" Style="{StaticResource StatusCellTextBlock}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
Using the style:
<Style x:Key="StatusCellTextBlock" TargetType="TextBlock">
<Style.Triggers>
<Trigger Property="Text" Value="Completed">
<Setter Property="Background" Value="Green"/>
<Setter Property="Foreground" Value="White" />
</Trigger>
</Style.Triggers>
</Style>

Trigger based on text property not working

Here is the xaml that I am working on:
<TextBlock Text="{Binding Title}" Margin="10,0,0,0" VerticalAlignment="Center">
<TextBlock.Resources>
<Style TargetType="{x:Type TextBlock}">
<Style.Triggers>
<Trigger Property="Text" Value="">
<Setter Property="Margin" Value="0" />
</Trigger>
</Style.Triggers>
</Style>
</TextBlock.Resources>
</TextBlock>
When text = "" I want to clear up the margin. But somehow it does not work.
You must move Margin="10,0,0,0" from TextBlock to setter of Style:
<Style TargetType="{x:Type TextBlock}">
<Setter Property="Margin" Value="10,0,0,0" />
<Style.Triggers>
<Trigger Property="Text" Value="">
<Setter Property="Margin" Value="0" />
</Trigger>
</Style.Triggers>
</Style>
Because local value has higher precedence order over style setters and triggers:
Property system coercion.
Active animations, or animations with a Hold behavior.
3. Local value.
TemplatedParent template properties.
Implicit style.
6. Style triggers.
Template triggers.
8. Style setters.
...
For more information, please see:
MSDN: Dependency Property Value Precedence

Same setters for different triggers

I have several elements and all of them should look the same if "X". The problem is, "X" is different for each element, so I can't easily use a shared style for them. But in all cases, the setters are the same, so I would like to have them all in one place. How can I achieve that?
More concretely, I have this code:
<TextBlock Name="text1">
<TextBlock.Style>
<Style TargetType="TextBlock">
<Style.Triggers>
<DataTrigger Binding="{Binding SelectedMonth, Converter={conv:IsNullConverter}}"
Value="False">
<Setter Property="Background" Value="RoyalBlue" />
<!-- possibly other setters -->
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
<TextBlock Name="text2">
<TextBlock.Style>
<Style TargetType="TextBlock">
<Style.Triggers>
<DataTrigger Binding="{Binding IsSelected}" Value="True">
<Setter Property="Background" Value="RoyalBlue" />
<!-- possibly other setters, same ones as above -->
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
Here, I would like to put the two Setters to some common place, so that I wouldn't have to always change them one by one. Is there some way to do that?
I tried putting a collection of Setter together and then using something like DataTrigger.Setters="{StaticResource SharedSetters}". This doesn't work, but if it did, it would be exactly what I want.
First of all, you could have a common style which would be the base (BasedOn attribute) for all the other styles. This style would contain a normal Trigger which is controlled by a custom boolean attached property, and the setters you want to reuse. Then, in the other styles, which are based on the base style, your DataTriggers would only set the attached property, and let the base style set all the other properties.
Assuming you have created the attached property IsSelected on the type Foo, the base style would look something like:
<Style x:Key="IsSelectedStyle" TargetType="TextBlock">
<Style.Triggers>
<Trigger Property="local:Foo.IsSelected" Value="True">
<Setter Property="Background" Value="RoyalBlue" />
<!-- possibly other setters -->
</Trigger>
</Style.Triggers>
</Style>
And then you would use it like this:
<TextBlock>
<TextBlock.Style>
<Style TargetType="TextBlock" BasedOn="{StaticResource IsSelectedStyle}">
<Style.Triggers>
<DataTrigger Binding="{Binding IsSelected}" Value="True">
<Setter Property="local:Foo.IsSelected" Value="True" />
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
Move value of your setters to static resources and set key in setters.
<SolidColorBrush x:Key="keyName" Color="RoyalBlue" />
and
<Setter Property="Background" Value="{StaticResource keyName}" />
in your code.
Now you can change brush of "keyName" and it affects all setters.

TextBox trigger to clear Text using a style

First, let me say I've been working with WPF for about a week. I want to style a TextBox so that when it is disable, it is cleared. This article explained how to do it, however I'm confused on how to set the generic style as a resource so that every TextBox can bind to a different property without repeating the style for each TextBox.
<Window.Resources>
<Style TargetType="{x:Type TextBox}" x:Key="style1">
<Setter Property="Text" Value="{What do I really put here?}" />
<Style.Triggers>
<Trigger Property="IsEnabled" Value="False">
<Setter Property="Text" Value="{x:Null}" />
</Trigger>
</Style.Triggers>
</Style>
</Window.Resources>
....
<TextBox Style="{StaticResource style1}" Text="{Binding SomeProperty}"/>
Thanks!
You won't be able to use the Text property like that. Setting the Text property explicitly on any TextBox that has that style will override the Text setter in the trigger (like you noticed).
If you only need the TextBox to be cleared and not the property it is binding to, then a workaround is to use an attached property (or Tag) for the text which you bind Text to in the Style.
Example..
<Style TargetType="{x:Type TextBox}" x:Key="style1">
<Setter Property="Text" Value="{Binding RelativeSource={RelativeSource Self},
Path=Tag}"/>
<Style.Triggers>
<Trigger Property="IsEnabled" Value="False">
<Setter Property="Text" Value="{x:Null}" />
</Trigger>
</Style.Triggers>
</Style>
Then a TextBox can use this Style like
<TextBox Style="{StaticResource style1}" Tag="{Binding SomeProperty}" />

Trigger for a ContextMenu Visibility?

I have a Listbox that starts off with no items inside. When a user uses the GUI to select a value from a combobox and clicks the add Button, the the listbox gets an item added. The Listbox also has a contextmenu set.
How can i use XAML to make sure that the Listbox.contextmenu.Visibility property set to hidden when there are no items in the listbox??
UPDATE
I actually used this code in the end. Please comment on its appropriatness
<Style TargetType="ListBox">
<Style.Resources>
<ContextMenu x:Key="cm">
<MenuItem Header="Buy"/>
<MenuItem Header="Sell"/>
</ContextMenu>
</Style.Resources>
<Setter Property="ContextMenu" Value="{StaticResource cm}"/>
<Style.Triggers>
<Trigger Property="ListBox.HasItems" Value="False">
<Setter Property="ContextMenu.Visibility" Value="Hidden"/>
</Trigger>
</Style.Triggers>
</Style>
I do not think that messing with the visibility of the ContextMenu is a good idea, just null out the whole menu if the list is empty.
e.g.
<Style TargetType="{x:Type ListBox}">
<Style.Resources>
<ContextMenu x:Key="cm">
<!-- Menu here -->
</ContextMenu>
</Style.Resources>
<Setter Property="ContextMenu" Value="{StaticResource cm}" />
<Style.Triggers>
<DataTrigger Binding="{Binding Items.Count, RelativeSource={RelativeSource Self}}" Value="0">
<Setter Property="ContextMenu" Value="{x:Null}" />
</DataTrigger>
</Style.Triggers>
</Style>
You could also choose to only define ContextMenus on the items themselves using the ItemContainerStyle, then no ContextMenu can be opened without items but that might of course not fit your scenario.

Resources