I want to set a DataTrigger for multiple DataTemplate but I am not sure if there is way to do that?
I have view structured like below
<ItemsControl ItemsSource="{Binding Collection}"
AlternationCount="2"
ItemTemplateSelector="{StaticResource RowSelector}" >
<local:HeaderTemplateSelector x:Key="RowSelector"
NormalTemplate="{StaticResource NormalTemplate}"
ExpanderTemplate1="{StaticResource ExpanderTemplate1}"
ExpanderTemplate2="{StaticResource ExpanderTemplate2}"
ExpanderTemplate3="{StaticResource ExpanderTemplate3}" />
<DataTemplate x:Key="NormalTemplate" ...>
<stackpanel x:Name="main"
<DataTemplate x:Key="ExpanderTemplate1" ...>
<Grid x:Name="main" ..>
<DataTemplate x:Key="ExpanderTemplate2" ...>
<Expander x:Name="main"
<DataTemplate x:Key="ExpanderTemplate3" ...>
<Textblock x:Name="main" ...>
I want this data trigger which provides alternative background for rows, the trigger defined below
<DataTemplate.Triggers>
<Trigger Property="ItemsControl.AlternationIndex" Value="0">
<Setter Property="Background" Value="Blue" TargetName="main"/>
</Trigger>
<Trigger Property="ItemsControl.AlternationIndex" Value="1">
<Setter Property="Background" Value="black" TargetName="main"/>
</Trigger>
</DataTemplate.Triggers>
I can add this to the end of each DataTemplate and it works fine but it mean that I have to duplicate the some code 4 times, is there any way to avoid that? (I know I can use datagrid but it is overkill for this simple structure)
You can try to set the trigger with the style:
<Style TargetType="Panel" x:Key="AlternatelyPainted">
<Style.Triggers>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ContentPresenter}}, Path=(ItemsControl.AlternationIndex)}" Value="0">
<Setter Property="Background" Value="Red"/>
</DataTrigger>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ContentPresenter}}, Path=(ItemsControl.AlternationIndex)}" Value="1">
<Setter Property="Background" Value="Blue"/>
</DataTrigger>
</Style.Triggers>
</Style>
(code thankfully stolen from this answer) and use it in your templates:
<DataTemplate x:Key="NormalTemplate" ...>
<StackPanel Style="{StaticResource AlternatelyPainted}">
...
</StackPanel>
</DataTemplate>
etc.
Edit: if you want the style to be universal, not Panel-specific, you can try the following trick:
<Style x:Key="AlternatelyPainted">
<Style.Triggers>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ContentPresenter}}, Path=(ItemsControl.AlternationIndex)}" Value="0">
<Setter Property="Panel.Background" Value="Red"/>
<Setter Property="Control.Background" Value="Red"/>
<Setter Property="TextBlock.Background" Value="Red"/>
</DataTrigger>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ContentPresenter}}, Path=(ItemsControl.AlternationIndex)}" Value="1">
<Setter Property="Panel.Background" Value="Blue"/>
<Setter Property="Control.Background" Value="Blue"/>
<Setter Property="TextBlock.Background" Value="Blue"/>
</DataTrigger>
</Style.Triggers>
</Style>
(removed the TargetType and added explicit types to the property names). Note that TextBlock doesn't inherit its Background, so we need an additional line for it.
Related
In my user control I want to have label displaying Text from resource depending on viewmodel property Location value and with different format.
I implemented it by defining DataTrigger for each Location value
<Style x:Key="LocationValueHelper" TargetType="{x:Type Label}">
<Style.Triggers>
<DataTrigger Binding="{Binding Location}" Value="HomeViewModel">
<Setter Property="Content" Value="" />
<Setter Property="ContentStringFormat" Value="{}{0}???"/>
</DataTrigger>
<DataTrigger Binding="{Binding Location}" Value="FirstViewModel">
<Setter Property="ContentStringFormat" Value="{}{0}+++"/>
<Setter Property="Content" Value="{localization:LanguageResource ResourceKey=First}" />
</DataTrigger>
<DataTrigger Binding="{Binding Location}" Value="SecondViewModel">
<Setter Property="ContentStringFormat" Value="{}{0}---"/>
<Setter Property="Content" Value="{localization:LanguageResource ResourceKey=Second}" />
</DataTrigger>
<DataTrigger Binding="{Binding Location}" Value="ThirdViewModel">
<Setter Property="ContentStringFormat" Value="{}{0}***"/>
<Setter Property="Content" Value="{localization:LanguageResource ResourceKey=Third}" />
</DataTrigger>
</Style.Triggers>
</Style>
<Label Foreground="White" HorizontalAlignment="Center" FontSize="40" Style="{StaticResource LocationValueHelper}" />
The problem is that format never changes from the first one (???) - HomeViewModel selected first - although text changing works.
I have
Home???
First???
Second???
Third???
instead of
Home???
First+++
Second---
Third***
There are a lot of discussions, using Labels and ContentStringFormat does not work like expected or desired:
WPF StringFormat on Label Content
StringFormat VS ContentStringFormat
...
The reason seems to be the template based implementation under the hood, since the content also could contain other data than strings. To solve your problem, my proposal is to use a TextBlock, which works quite smooth like this:
<StackPanel>
<CheckBox IsChecked="{Binding MyFlag}"/>
<TextBlock HorizontalAlignment="Center" FontSize="40">
<TextBlock.Style>
<Style TargetType="{x:Type TextBlock}">
<Style.Triggers>
<DataTrigger Binding="{Binding MyFlag}" Value="False">
<Setter Property="Text" Value="{Binding Path=MyText, StringFormat='{}{0}+++'}" />
</DataTrigger>
<DataTrigger Binding="{Binding MyFlag}" Value="True">
<Setter Property="Text" Value="{Binding Path=MyText, StringFormat='{}{0}???'}" />
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
</StackPanel>
I have a C# WPF project. I am using an ObservableCollection<T> in my viewmodel class.
I have a ListView in xaml file. There is TextBlock inside the ListView binding to the ObservableCollection<T>.
I want to collapse them by DataTrigger. The result is that those TextBlock is collapsed but still using space. I would like to ask how to fix it?
Here is the ListView I am using:
<ListView Grid.Row="1" ItemsSource="{Binding ServiceObjects}">
<ListView.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding ServiceName}">
<TextBlock.Style>
<Style TargetType="TextBlock">
<Setter Property="Visibility" Value="Collapsed" />
<Setter Property="Foreground" Value="Black" />
<Style.Triggers>
<DataTrigger Binding="{Binding IsCollapsed}" Value="true">
<Setter Property="Foreground" Value="#FFBF00" />
<Setter Property="Visibility" Value="Visible" />
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
Try this:
<Style TargetType="ListBoxItem" x:Key="LstBoxItemStyle">
<Setter Property="MinHeight"
Value="0" />
<Setter Property="IsEnabled"
Value="False" />
<Style.Triggers>
<DataTrigger Binding="{Binding IsCollapsed}" Value="True">
<Setter Property="IsEnabled"
Value="True" />
</DataTrigger>
</Style.Triggers>
</Style>
and
<ListView Grid.Row="1"
ItemsSource="{Binding ServiceObjects}"
ItemContainerStyle="{StaticResource LstBoxItemStyle}">
Or use filters https://learn.microsoft.com/pl-pl/dotnet/desktop/wpf/data/how-to-filter-data-in-a-view?view=netframeworkdesktop-4.8
i have this CheckBox
<CheckBox VerticalAlignment="Center" Name="CheckItem"
IsChecked="{Binding ElementName=Faccette, Path=Text.isEmpty, Mode=OneWay}"
/>
Faccette is a TextBlock, i need the opposite of the Text.isEmpty.
Is it possible without a Converter?
You may use a Style with DataTriggers:
<CheckBox>
<CheckBox.Style>
<Style TargetType="CheckBox">
<Setter Property="IsChecked" Value="True"/>
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=Faccette, Path=Text}" Value="">
<Setter Property="IsChecked" Value="False"/>
</DataTrigger>
<DataTrigger Binding="{Binding ElementName=Faccette, Path=Text}" Value="{x:Null}">
<Setter Property="IsChecked" Value="False"/>
</DataTrigger>
</Style.Triggers>
</Style>
</CheckBox.Style>
</CheckBox>
I have a data template which defines a line to display for every NodePairViewModel drawn inside by a canvas ItemsPanel. The line colour should change when the IsChecked Property of the NodePairViewModel being represented changes as defined in the style (see below) however when this property is set the colour does not change. INotifyPropertyChanged is implemented and I have verified that this event is firing. I cannot see anything obviously wrong and the same setup works for NodeViewModels which are also drawn in the same itemspanel.
<DataTemplate DataType="{x:Type vm:NodePairViewModel}">
<Line Canvas.ZIndex="2" Name="Arc" Stroke="Blue" StrokeThickness="3"
X1="{Binding Path=NodeA.PositionX, Mode=OneWay, Converter={StaticResource NodeLinkXConverter}}"
X2="{Binding Path=NodeB.PositionX, Mode=OneWay, Converter={StaticResource NodeLinkXConverter}}"
Y1="{Binding Path=NodeA.PositionY, Mode=OneWay, Converter={StaticResource NodeLinkYConverter}}"
Y2="{Binding Path=NodeB.PositionY, Mode=OneWay, Converter={StaticResource NodeLinkYConverter}}"
MouseLeftButtonDown="Arc_MouseLeftButtonDown" MouseLeftButtonUp="Arc_MouseLeftButtonUp">
<Line.Style>
<Style TargetType="Line">
<Style.Triggers>
<DataTrigger Binding="{Binding Path=IsChecked}" Value="True">
<Setter Property="Stroke" Value="Orange"></Setter>
</DataTrigger>
<DataTrigger Binding="{Binding Path=IsSelected, Mode=OneWay}" Value="True">
<Setter Property="Stroke" Value="Red"/>
</DataTrigger>
<DataTrigger Binding="{Binding Path=IsRejected, Mode=OneWay}" Value="True">
<Setter Property="Stroke" Value="LightGray"/>
</DataTrigger>
<DataTrigger Binding="{Binding Path=IsVisited}" Value="True">
<Setter Property="Stroke" Value="LightGreen"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Line.Style>
</Line>
</DataTemplate>
<ItemsControl Name="NodeSpace" ItemsSource="{Binding GraphItems}" Margin="5" MouseMove="NodeSpace_MouseMove" MouseDown="NodeSpace_MouseDown" MouseLeftButtonUp="NodeSpace_MouseLeftButtonUp" MouseLeftButtonDown="NodeSpace_MouseLeftButtonDown" MouseRightButtonDown="NodeSpace_MouseRightButtonDown" RenderTransformOrigin="0.5,0.5" Grid.Column="1" Grid.Row="1">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Canvas Name="GraphSurface" Background="#FFFFFE"></Canvas>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemContainerStyle>
<Style TargetType="ContentPresenter">
<Setter Property="Canvas.Left">
<Setter.Value>
<Binding Mode="OneWay" Path="PositionX" Converter="{StaticResource XInverseConverter}"/>
</Setter.Value>
</Setter>
<Setter Property="Canvas.Top">
<Setter.Value>
<Binding Mode="OneWay" Path="PositionY" Converter="{StaticResource YInverseConverter}"/>
</Setter.Value>
</Setter>
</Style>
</ItemsControl.ItemContainerStyle>
</ItemsControl>
And the IsChecked property itself
private bool ArcIsChecked = false;
public bool IsChecked
{
get
{
return ArcIsChecked;
}
set
{
ArcIsChecked = value;
RaisePropertyChanged("IsChecked");
}
}
You need to move default value from Line
<Line ... Stroke="Blue">
into Style as Setter
<Line
Canvas.ZIndex="2"
Name="Arc"
StrokeThickness="3"
X1="{Binding Path=NodeA.PositionX, Mode=OneWay, Converter={StaticResource NodeLinkXConverter}}"
X2="{Binding Path=NodeB.PositionX, Mode=OneWay, Converter={StaticResource NodeLinkXConverter}}"
Y1="{Binding Path=NodeA.PositionY, Mode=OneWay, Converter={StaticResource NodeLinkYConverter}}"
Y2="{Binding Path=NodeB.PositionY, Mode=OneWay, Converter={StaticResource NodeLinkYConverter}}"
MouseLeftButtonDown="Arc_MouseLeftButtonDown"
MouseLeftButtonUp="Arc_MouseLeftButtonUp">
<Line.Style>
<Style TargetType="{x:Type Line}">
<Setter Property="Stroke" Value="Blue"/>
<Style.Triggers>
<DataTrigger Binding="{Binding Path=IsChecked}" Value="True">
<Setter Property="Stroke" Value="Orange"/>
</DataTrigger>
<DataTrigger Binding="{Binding Path=IsSelected, Mode=OneWay}" Value="True">
<Setter Property="Stroke" Value="Red"/>
</DataTrigger>
<DataTrigger Binding="{Binding Path=IsRejected, Mode=OneWay}" Value="True">
<Setter Property="Stroke" Value="LightGray"/>
</DataTrigger>
<DataTrigger Binding="{Binding Path=IsVisited}" Value="True">
<Setter Property="Stroke" Value="LightGreen"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Line.Style>
</Line>
according to Dependency Property Setting Precedence List style trigger is lower in hierarchy then local value so it won't be able to overwrite your default (local) value
I would like to change the background color of a cell depending on the alternation index of the DataGrid. My template looks like this:
<DataTemplate x:Key="MyTemplate">
<Label x:Name="MyLabel">foobar</Label>
<DataTemplate.Triggers>
<Trigger Property="DataGridRow.AlternationIndex" Value="0">
<Setter Property="Background" Value="Blue" TargetName="MyLabel"/>
</Trigger>
<Trigger Property="DataGridRow.AlternationIndex" Value="1">
<Setter Property="Background" Value="Red" TargetName="MyLabel"/>
</Trigger>
</DataTemplate.Triggers>
</DataTemplate>
Whereas my DataGrid has set the property:
<DataGrid AlternationCount="2"> ... </DataGrid>
The blue background works, but all cells have a blue background and no cell a red background. What is missing here?
You will have to use DataTrigger instead of Triggers
<DataTemplate x:Key="MyTemplate">
<Label x:Name="MyLabel">foobar</Label>
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource Self}, Path=(ItemsControl.AlternationIndex)}" Value="0">
<Setter Property="Background" Value="Blue" TargetName="MyLabel"/>
</DataTrigger >
<DataTrigger Binding="{Binding RelativeSource={RelativeSource Self}, Path=(ItemsControl.AlternationIndex)}" Value="1">
<Setter Property="Background" Value="Red" TargetName="MyLabel"/>
</DataTrigger >
</DataTemplate.Triggers>
</DataTemplate>