DataTriger Style Setter for Image Element - wpf

I have an image element that is populated via databinding - if the binding returns a path, the image is drawn. If not (path comes in an an empty string), we get no image.
<Image Source="{Binding Path=.Screenshot, Mode=OneWay}" Stretch="Fill" Margin="5,5,5,5" />
That works well, except that the margin is applied to the layout no matter what (which looks bad for empty images). I figured I'd do a DataTrigger instead that only applies the margin is the path is not empty, but:
Image doesn't seem to have a Style (actually it does, ignore this part)
I don't know how to test for "string is not empty".
What I'd like to do is something like the pseudo-XAML below. Is that possible in XAML?
<Image Source="{Binding Path=.Screenshot, Mode=OneWay}" Stretch="Fill" >
<Image.Style>
<Style TargetType="Image">
<Setter Property="Margin" Value="0,0,0,0" />
<Style.Triggers>
<DataTrigger Binding="{Binding Path=.Screenshot}" Value="!NullOrEmpty">
<Setter Property="Margin" Value="5,5,5,5" />
</DataTrigger>
</Style.Triggers>
</Style>
</Image.Style>
Should I be maybe using an entirely different approach for this?

Do it the other way round, by having a DataTrigger for the values null and "":
<Style TargetType="Image">
<Setter Property="Margin" Value="5,5,5,5" />
<Style.Triggers>
<DataTrigger Binding="{Binding Path=Screenshot}" Value="{x:Null}">
<Setter Property="Margin" Value="0,0,0,0" />
</DataTrigger>
<DataTrigger Binding="{Binding Path=Screenshot}" Value="">
<Setter Property="Margin" Value="0,0,0,0" />
</DataTrigger>
</Style.Triggers>
</Style>
You may probably also just have a Trigger on the Images's Source property:
<Style TargetType="Image">
<Setter Property="Margin" Value="5,5,5,5" />
<Style.Triggers>
<Trigger Property="Source" Value="{x:Null}">
<Setter Property="Margin" Value="0,0,0,0" />
</Trigger>
</Style.Triggers>
</Style>

Related

Toggle button - change icon on click

I have an app in WPF. I am using entypo icons and i decalred one as a resource:
<Grid.Resources>
<iconPacks:Entypo x:Key="PlayIcon" Width="50" Height="30" Kind="ControllerPlay"></iconPacks:Entypo>
</Grid.Resources>
Let's say I have two icons like this ( play/pause icon) and I want to change between them when the user clicks on ToggleButton. I came up with something like this, but unfortunately, it's not working:
<ToggleButton>
<Image>
<Image.Style>
<Style TargetType="{x:Type Image}">
<Style.Triggers>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource AncestorType=ToggleButton}, Path=IsChecked}"
Value="true">
<Setter Property="Source"
Value="{StaticResource PauseIcon}" />
</DataTrigger>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource AncestorType=ToggleButton}, Path=IsChecked}"
Value="false">
<Setter Property="Source"
Value="{StaticResource PlayIcon}" />
</DataTrigger>
</Style.Triggers>
</Style>
</Image.Style>
</Image>
</ToggleButton>
Could anyone tell me if I can achieve it like this (with slight modifications) or point me into right direction?
You can't set the Source of an Image to a PackIconEntypo. Set the Content property of the ToggleButton instead:
<ToggleButton>
<ToggleButton.Style>
<Style TargetType="{x:Type ToggleButton}">
<Setter Property="Content" Value="{StaticResource PlayIcon}" />
<Style.Triggers>
<Trigger Property="IsChecked" Value="true">
<Setter Property="Content" Value="{StaticResource PauseIcon}" />
</Trigger>
</Style.Triggers>
</Style>
</ToggleButton.Style>
</ToggleButton>

WPF DataGrid Header contents from style only display once per trigger

An odd problem is occurring with my WPF DataGrid Headers' contents. The XAML for the style looks like this:
<DataGrid.RowStyle>
<Style TargetType="DataGridRow">
<Style.Triggers>
<DataTrigger Binding="{Binding Type, Converter={StaticResource EnumToStringConverter}}" Value="Default">
<Setter Property="Background" Value="White"/>
</DataTrigger>
<DataTrigger Binding="{Binding Type, Converter={StaticResource EnumToStringConverter}}" Value="Error">
<Setter Property="Background" Value="#F2DEDE"/>
<Setter Property="Header">
<Setter.Value>
<Image Source="{StaticResource IconError}" Width="16" Height="16"></Image>
</Setter.Value>
</Setter>
</DataTrigger>
<DataTrigger Binding="{Binding Type, Converter={StaticResource EnumToStringConverter}}" Value="Warning">
<Setter Property="Background" Value="#FCF8E3"/>
<Setter Property="Header">
<Setter.Value>
<Image Source="{StaticResource IconWarning}" Width="16" Height="16"></Image>
</Setter.Value>
</Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</DataGrid.RowStyle>
Error and warning icons should be on every row of those types, with standard rows being blank. However, the output looks like so:
The icons only seem to appear once per trigger, though the trigger is evidently firing as the row colouring is correct.
Snoop shows that the image is simply not inserted into each of the other Headers.
This still happens when replacing the resource image with a path, and when replacing the images entirely with TextBlock elements.
What's going on here? Is there a better way I'm missing?
Edit: For anyone finding this after the fact, the StaticResource icons are BitmapImage instances, not paths.
A UIElement such as an Image can only appear once in the visual tree.
You could define the images and non-shared resources:
<DataGrid>
<DataGrid.Resources>
<Image x:Key="errorImg" x:Shared="False" Source="{StaticResource IconError}" Width="16" Height="16"></Image>
<Image x:Key="warnImg" x:Shared="False" Source="{StaticResource IconWarning}" Width="16" Height="16"></Image>
</DataGrid.Resources>
<DataGrid.RowStyle>
<Style TargetType="DataGridRow">
<Style.Triggers>
<DataTrigger Binding="{Binding Type, Converter={StaticResource EnumToStringConverter}}" Value="Default">
<Setter Property="Background" Value="White"/>
</DataTrigger>
<DataTrigger Binding="{Binding Type, Converter={StaticResource EnumToStringConverter}}" Value="Error">
<Setter Property="Background" Value="#F2DEDE"/>
<Setter Property="Header" Value="{StaticResource errorImg}" />
</DataTrigger>
<DataTrigger Binding="{Binding Type, Converter={StaticResource EnumToStringConverter}}" Value="Warning">
<Setter Property="Background" Value="#FCF8E3"/>
<Setter Property="Header" Value="{StaticResource warnImg}" />
</DataTrigger>
</Style.Triggers>
</Style>
</DataGrid.RowStyle>
</DataGrid>
In addition to the chosen answer here, I managed to get this working by replacing the direct set of the Header value with a template instead.
<DataTrigger Binding="{Binding Type, Converter={StaticResource EnumToStringConverter}}" Value="Error">
<Setter Property="Background" Value="#F2DEDE"/>
<Setter Property="HeaderTemplate">
<Setter.Value>
<DataTemplate>
<Image Source="{StaticResource IconError}" Width="16" Height="16"></Image>
</DataTemplate>
</Setter.Value>
</Setter>
</DataTrigger>

XAML: x:Array of static resources

I have a number of BitmapImages declared in my ResourceDictionary:
<BitmapImage x:Key="LockImageSource" UriSource="img/lock.png" />
<BitmapImage x:Key="UnlockImageSource" UriSource="img/unlock.png" />
...
My converter takes an array parameter of two ImageSource elements to choose one of images to show depending on passed value:
<Image Source="{Binding Path=IsLocked,
Converter={StaticResource AlteringConverter},
ConverterParameter={StaticResource LockUnlockImageSourcePair}}"
Width="16" Height="16" />
How should LockUnlockImageSourcePair resource look like?
<x:Array x:Key="LockUnlockImageSourcePair" Type="{x:Type BitmapImage}">
<??? />
<??? />
</x:Array>
Instead of a Binding with a Converter and complicated ConverterParameter, you could use this simple Style with a DataTrigger:
<Image Width="16" Height="16">
<Image.Style>
<Style TargetType="Image">
<Setter Property="Source" Value="{StaticResource UnlockImageSource}"/>
<Style.Triggers>
<DataTrigger Binding="{Binding IsLocked}" Value="True">
<Setter Property="Source" Value="{StaticResource LockImageSource}"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Image.Style>
</Image>
If you want to reuse the Style for multiple Image controls, you may declare it as resource:
<Style x:Key="LockUnlockImageStyle" TargetType="Image">
<Setter Property="Width" Value="16"/>
<Setter Property="Height" Value="16"/>
<Setter Property="Source" Value="{StaticResource UnlockImageSource}"/>
<Style.Triggers>
<DataTrigger Binding="{Binding IsLocked}" Value="True">
<Setter Property="Source" Value="{StaticResource LockImageSource}"/>
</DataTrigger>
</Style.Triggers>
</Style>
...
<Image Style="{StaticResource LockUnlockImageStyle}"/>

WPF Image Style Trigger - small issue

i'm having a small issue with an style trigger on an image. The trigger does change the source of the image, but when it does i lose the streght="fill" property. I've also tried setting it in the trigger, but it seem to ignore it. What am i missing here?
<Image x:Name="xBackground"
Height="99.5"
Width="271"
Stretch="Fill">
<Image.Style>
<Style TargetType="{x:Type Image}">
<Setter Property="Source" Value="Green.png" />
<Style.Triggers>
<DataTrigger Binding="{Binding HasAlarm}" Value="true">
<Setter Property="Source" Value="Red.png" />
</DataTrigger>
</Style.Triggers>
</Style>
</Image.Style>
</Image>

Conditional Trigger is it possible?

I have a situation where I need to check a property "HasDelivered" if true.
the foreground color of my textBlock should be green else red.
Any ideas or suggestions
Use a style with a data trigger:
<TextBlock ...>
<TextBlock.Style>
<Style TargetType="TextBlock">
<Setter Property="Foreground" Value="Red" />
<Style.Triggers>
<DataTrigger Binding="{Binding HasDelivered}" Value="True">
<Setter Property="Foreground" Value="Green" />
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>

Resources