Controlling DataTrigger ExitActions - wpf

I have a CheckBox (placed above an image) whose Opacity is set to 0 by default. When IsMouseOver is true, the CheckBox Opacity property is animated from 0 to 1 and reversed when IsMouseOver is false. However, if the CheckBox IsSelected is true, then the reverse animation(i.e DataTrigger.ExitActions) should not execute. i.e CheckBox should remain visible if it is checked. In my XAML the checkbox is disappearing even when it is checked.
Following is my XAML:
<CheckBox VerticalAlignment="Center" HorizontalAlignment="Center">
<CheckBox.Style>
<Style TargetType="{x:Type CheckBox}">
<Setter Property="Opacity" Value="0" />
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=imageGrid, Path=IsMouseOver}" Value="True">
<DataTrigger.EnterActions>
<BeginStoryboard>
<Storyboard Timeline.DesiredFrameRate="100">
<DoubleAnimation Storyboard.TargetProperty="Opacity" From="0" To="1" Duration="00:00:00.400" />
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
<DataTrigger.ExitActions>
<BeginStoryboard>
<Storyboard Timeline.DesiredFrameRate="100">
<DoubleAnimation Storyboard.TargetProperty="Opacity" From="1" To="0" Duration="00:00:00.400" />
</Storyboard>
</BeginStoryboard>
</DataTrigger.ExitActions>
</DataTrigger>
<DataTrigger Binding="{Binding IsChecked, RelativeSource={RelativeSource Mode=Self}}" Value="true">
<Setter Property="Opacity" Value="1" />
</DataTrigger>
</Style.Triggers>
</Style>
</CheckBox.Style>
</CheckBox>
Implemented the way #feO2x suggested. However it is not working as expected. Got it work in a simple application. However I intend to use the checkbox in a ListViewItem. Following is my XAML:
<Style x:Key="ThumbView_ItemContainerStyle" TargetType="{x:Type ListViewItem}">
<Setter Property="Background" Value="Transparent"/>
<Setter Property="HorizontalContentAlignment" Value="Center"/>
<Setter Property="VerticalContentAlignment" Value="Center"/>
<Setter Property="Padding" Value="0"/>
<Setter Property="Height" Value="175" />
<Setter Property="Width" Value="125" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ListViewItem}">
<Grid Background="Pink" x:Name="mygrid">
<Rectangle Name="LBRect" Fill="Transparent" Opacity="0.195" />
<Image Name="posterImg" Margin="0,0,0,0" RenderOptions.BitmapScalingMode="HighQuality" SnapsToDevicePixels="True" HorizontalAlignment="Center" VerticalAlignment="Center" Source="{Binding Path=ProfilePic}" Height="125" MaxWidth="125" />
<CheckBox IsChecked="{Binding IsSelected, Mode=TwoWay}" VerticalAlignment="Center" HorizontalAlignment="Center" x:Name="CheckBox">
<CheckBox.RenderTransform>
<ScaleTransform ScaleX="1.1" ScaleY="1.1" />
</CheckBox.RenderTransform>
<CheckBox.Style>
<Style TargetType="{x:Type CheckBox}" BasedOn="{StaticResource ThumbViewCheckBoxStyle}">
<Setter Property="Opacity" Value="0" />
<Style.Triggers>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding Path=IsMouseOver, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type ListViewItem}}}" Value="False" />
<Condition Binding="{Binding ElementName=CheckBox, Path=IsChecked}" Value="False" />
</MultiDataTrigger.Conditions>
<MultiDataTrigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="Opacity" To="0.0" Duration="0:0:0.4" />
</Storyboard>
</BeginStoryboard>
</MultiDataTrigger.EnterActions>
<MultiDataTrigger.ExitActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="Opacity" To="1.0" Duration="0:0:0.4" />
</Storyboard>
</BeginStoryboard>
</MultiDataTrigger.ExitActions>
</MultiDataTrigger>
</Style.Triggers>
</Style>
</CheckBox.Style>
</CheckBox>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>

This can be achieved with IMultiValueConverter in place. Pass two bindings to it i.e. IsMouseOver and IsChecked.
Converter:
public class MyConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter,
CultureInfo culture)
{
bool isMouseOver = (bool)values[0];
bool isChecked = (bool)values[1];
return isChecked || isMouseOver;
}
public object[] ConvertBack(object value, Type[] targetTypes,
object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
XAML:
<DataTrigger Value="True">
<DataTrigger.Binding>
<MultiBinding Converter="{StaticResource MyConverter}">
<Binding Path="IsMouseOver" ElementName="imageGrid"/>
<Binding Path="IsChecked" RelativeSource="{RelativeSource Self}"/>
</MultiBinding>
</DataTrigger.Binding>
<!-- Rest same trigger -->
</DataTrigger>
Of course you need to add MyValueConverter instance as resource in your XAML file.

Update February 26th: In your Style that is not working, you reference the checkbox the wrong way. ElementName can only be used in a binding if the target control was given a name and is accessible within the current WPF XAML namescope (you can learn about XAML namescopes here).
You should use a relative source within your binding to reference the CheckBox correctly: RelativeSource={RelativeSource Self}. The following ListView style shows the whole example:
<Style x:Key="ListViewItemStyle" TargetType="{x:Type ListViewItem}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Border Background="{TemplateBinding Background}">
<Grid Margin="{TemplateBinding Padding}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="150" />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<TextBlock Text="{Binding Name}" TextTrimming="CharacterEllipsis" />
<CheckBox IsChecked="{Binding IsSelected}" Grid.Column="1">
<CheckBox.Style>
<Style TargetType="{x:Type CheckBox}">
<Setter Property="Opacity" Value="0.0" />
<Style.Triggers>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding Path=IsMouseOver, RelativeSource={RelativeSource AncestorType=ListBoxItem}}"
Value="False" />
<Condition Binding="{Binding Path=IsChecked, RelativeSource={RelativeSource Self}}"
Value="False" />
</MultiDataTrigger.Conditions>
<MultiDataTrigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="Opacity"
To="0.0" Duration="0:0:0.4" />
</Storyboard>
</BeginStoryboard>
</MultiDataTrigger.EnterActions>
<MultiDataTrigger.ExitActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="Opacity"
To="1.0" Duration="0:0:0.4" />
</Storyboard>
</BeginStoryboard>
</MultiDataTrigger.ExitActions>
</MultiDataTrigger>
</Style.Triggers>
</Style>
</CheckBox.Style>
</CheckBox>
</Grid>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
You can download a working example from here (DropBox link).
Original Answer:
You can use a MultiDataTrigger within your style to accomplish the behavior you want. Have a look at the following code:
<CheckBox Grid.Column="1" VerticalAlignment="Center" HorizontalAlignment="Center" Content="Check Me" Name="CheckBox">
<CheckBox.Style>
<Style TargetType="{x:Type CheckBox}">
<Setter Property="Opacity" Value="0.0" />
<Style.Triggers>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding ElementName=Rectangle, Path=IsMouseOver}" Value="False" />
<Condition Binding="{Binding ElementName=CheckBox, Path=IsChecked}" Value="False" />
</MultiDataTrigger.Conditions>
<MultiDataTrigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="Opacity"
To="0.0"
Duration="0:0:0.4"
BeginTime="0:0:0.5" />
</Storyboard>
</BeginStoryboard>
</MultiDataTrigger.EnterActions>
<MultiDataTrigger.ExitActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="Opacity"
To="1.0"
Duration="0:0:0.4" />
</Storyboard>
</BeginStoryboard>
</MultiDataTrigger.ExitActions>
</MultiDataTrigger>
</Style.Triggers>
</Style>
</CheckBox.Style>
</CheckBox>
A MultiDataTrigger is used to describe two conditions that have to be true for the enter actions to be executed. In this case I choose that the IsMouseOver property of a rectangle as well as the IsChecked property of the checkbox must both be false. Thus the checkbox is faded in when the mouse is over the rectangle and stays visible when it becomes checked.
You can download my full sample here (it's a Dropbox link).
Hope this helps you. If you have any questions, please feel free to leave a comment.

Related

Not all DataTriggers of StackPanel are working

I have a button which shows a text and an image:
For this I use a StackPanel with a TextBlock and an Image inside of it.
When the variable "ActiveState" changes the Background of the StackPanel should change too, for this I use DataTriggers
ActiveState=0 -> Red /
ActiveState=1 -> Blue /
ActiveState=2 -> blinking Blue (for this I use a Storyboard and a Color Animation)
The blinking Trigger (Value=2) is working fine, but the two other Triggers (Value=0 + Value=1) are not working.
When I remove the Background of the Stackpanel (Background="Transparent") the first two Triggers are working but the last one get the following Exception:
An unhandled exception of type 'System.InvalidOperationException' occurred in PresentationFramework.dll
Additional information: Background property does not point to a dependencyobject in path '(0).(1)'
This is my code:
<Button>
<Button.Template>
<ControlTemplate TargetType="Button">
<StackPanel Orientation="Horizontal" Name="SelectButtonStackpanel" Background="Transparent">
<TextBlock Text="{Binding Text}"/>
<Image Source="{Binding Image}" Stretch="Uniform" Height="40" Width="40"/>
<StackPanel.Style>
<Style TargetType="{x:Type StackPanel}">
<Style.Triggers>
<DataTrigger Binding="{Binding ActiveState}" Value="0">
<Setter Property="Background" Value="Red"/>
</DataTrigger>
<DataTrigger Binding="{Binding ActiveState}" Value="1">
<Setter Property="Background" Value="Blue"/>
</DataTrigger>
<DataTrigger Binding="{Binding ActiveState}" Value="2">
<DataTrigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<ColorAnimation
Storyboard.TargetProperty="(StackPanel.Background).(SolidColorBrush.Color)"
To="Blue" Duration="0:0:1" AutoReverse="True" RepeatBehavior="Forever"
>
</ColorAnimation>
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
<DataTrigger.ExitActions>
<BeginStoryboard>
<Storyboard>
<ColorAnimation
Storyboard.TargetProperty="(StackPanel.Background).(SolidColorBrush.Color)"
Duration="0:0:1"
>
</ColorAnimation>
</Storyboard>
</BeginStoryboard>
</DataTrigger.ExitActions>
</DataTrigger>
</Style.Triggers>
</Style>
</StackPanel.Style>
</StackPanel>
</ControlTemplate>
</Button.Template>
</Button>
Do you have any idea how I get all three Triggers working?
best regards
Phil
When you directly set Background="Transparent" on the StackPanel, this has higher precedence than a value set from a Style Setter. So remove the direct assignment and add another Setter for the default Background.
Besides that, if you want to animate Background.Color you should always explictly assign SolidColorBrushes instead of predefined Brushes like Background="Red".
<StackPanel Orientation="Horizontal" Name="SelectButtonStackpanel">
<StackPanel.Style>
<Style TargetType="StackPanel">
<Setter Property="Background">
<Setter.Value>
<SolidColorBrush Color="Transparent"/>
</Setter.Value>
</Setter>
<Style.Triggers>
<DataTrigger Binding="{Binding ActiveState}" Value="0">
<Setter Property="Background">
<Setter.Value>
<SolidColorBrush Color="Red"/>
</Setter.Value>
</Setter>
</DataTrigger>
<DataTrigger Binding="{Binding ActiveState}" Value="1">
<Setter Property="Background">
<Setter.Value>
<SolidColorBrush Color="Blue"/>
</Setter.Value>
</Setter>
</DataTrigger>
<DataTrigger Binding="{Binding ActiveState}" Value="2">
<DataTrigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<ColorAnimation
Storyboard.TargetProperty="Background.Color"
To="Blue" Duration="0:0:1"
AutoReverse="True" RepeatBehavior="Forever">
</ColorAnimation>
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
</DataTrigger>
</Style.Triggers>
</Style>
</StackPanel.Style>
</StackPanel>

Set DoubleAnimation with Binding to an external object

I have the following Style that contains an animation used to expand a Border:
<Style TargetType="{x:Type Border}" x:Key="BorderAnimations">
<Setter Property="FlowDirection" Value="RightToLeft" />
<Style.Triggers>
<EventTrigger RoutedEvent="MouseEnter">
<BeginStoryboard>
<Storyboard >
<DoubleAnimation
Storyboard.TargetProperty="Width"
BeginTime="0:0:0"
From="" To="420" Duration="0:0:0.2" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
<EventTrigger RoutedEvent="MouseLeave">
<BeginStoryboard>
<Storyboard >
<DoubleAnimation
Storyboard.TargetProperty="Width"
BeginTime="0:0:0"
From="420" To="90" Duration="0:0:0.2" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Style.Triggers>
</Style>
I want to set the From property to the generics' button Width. Here I have the generic Button style:
<Style TargetType="{x:Type Button}">
<Setter Property="Foreground" Value="White" />
<Setter Property="FontSize" Value="80" />
<Setter Property="HorizontalAlignment" Value="Right"/>
<Setter Property="VerticalAlignment" Value="Center" />
<Setter Property="Background" Value="{DynamicResource MainBlue}" />
<Setter Property="Height" Value="{Binding
ElementName=MainUCPanel, Path=Height,
Converter={StaticResource ArithmeticConverter},
ConverterParameter=Int32.Parse(values[0]) / 6}" />
<Setter Property="Width" Value="{Binding Path=Height, RelativeSource={RelativeSource Self}}" />
<Setter Property="DataContext" Value="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type view:DesktopUCMain}}, Path=DataContext}" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Border Background="{TemplateBinding Background}" BorderBrush="Transparent" BorderThickness="0" CornerRadius="5" x:Name="bd">
<ContentPresenter HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
Margin="5" VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" RecognizesAccessKey="True"/>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Cursor" Value="Hand" />
<Trigger.ExitActions>
<BeginStoryboard>
<Storyboard>
<ColorAnimation Storyboard.TargetProperty="(Button.Background).(SolidColorBrush.Color)" To="#FF1E90FF" Duration="0:0:0.2" />
</Storyboard>
</BeginStoryboard>
</Trigger.ExitActions>
<Trigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<ColorAnimation Storyboard.TargetProperty="(Button.Background).(SolidColorBrush.Color)" To="#27ace3" Duration="0:0:0.2" />
</Storyboard>
</BeginStoryboard>
</Trigger.EnterActions>
</Trigger>
</Style.Triggers>
</Style>
I've tried to set it as:
From="{Binding RelativeSource={RelativeSource AncestorType={x:Type Button}}, Path=ActualWidth}"
But it's not working...
NOTE: these styles are located on a ResourceDictionary.xaml. How can I achieve the animation to get this width?
EDIT: Thrown exception:
Cannot freeze this Storyboard timeline tree for use across threads.

datatrigger cannot set bound property

I have 2 checkboxes (chkMfsUi and chkMfs). When I check chkMfsUi, I also want to check chkMfs (and disable it).
I tried it with a datatrigger:
<Style x:Key="MfsCheckBoxStyle" TargetType="CheckBox">
<Setter Property="IsEnabled" Value="True" />
<Setter Property="IsChecked" Value="False" />
<Style.Triggers>
<DataTrigger Binding="{Binding IsChecked, ElementName=chkMfsUi}" Value="True">
<Setter Property="IsEnabled" Value="False" />
<Setter Property="IsChecked" Value="True" />
</DataTrigger>
</Style.Triggers>
</Style>
And these are my checkboxes:
<StackPanel>
<CheckBox Name="chkMfsUi"
Checked="CheckBox_Checked"
Content="MFS - UI"
IsChecked="{Binding MfsUi}"
Unchecked="CheckBox_Checked" />
<CheckBox Content="MFS" IsChecked="{Binding Mfs}" Style="{StaticResource MfsCheckBoxStyle}" />
</StackPanel>
The IsEnabled property work fine, but the IsChecked doesn't. Maybe because it is bound? I am also using INotifyPropertyChanged with these properties.
If I set it in code behind, it works, but is it possible with a trigger though?
Regards,
Alfons
EDIT:
As appeared from the current answers, my question seems to be incomplete. I need to have the following three states:
AND last but not least: Both checkboxes' IsChecked properties must be bound! (This is the moment when the trouble comes in)
Try this
<StackPanel Grid.Column="1">
<CheckBox x:Name="chkMfsUi" IsChecked="True"></CheckBox>
<CheckBox x:Name="chkMfs" IsChecked="{Binding ElementName=chkMfsUi,Path=IsChecked}">
<CheckBox.Style>
<Style TargetType="CheckBox">
<Style.Triggers>
<Trigger Property="IsChecked" Value="True">
<Setter Property="IsEnabled" Value="False"/>
</Trigger>
</Style.Triggers>
</Style>
</CheckBox.Style>
</CheckBox>
Another Method : By using style in resource
Add this namespace first xmlns:Globalvaribale="clr-namespace:System;assembly=mscorlib"
<Window x:Class="WpfApplication2.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:Globalvaribale="clr-namespace:System;assembly=mscorlib">
<Window.Resources>
<Globalvaribale:String x:Key="chkMfsUi">chkMfsUi</Globalvaribale:String>
<Style x:Key="chkMfsstyle" TargetType="CheckBox">
<Style.Triggers>
<DataTrigger Binding="{Binding IsChecked,ElementName={StaticResource chkMfsUi}}" Value="True">
<Setter Property="IsEnabled" Value="False"/>
<Setter Property="IsChecked" Value="True"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Window.Resources>
<StackPanel Grid.Column="1">
<CheckBox x:Name="chkMfsUi"></CheckBox>
<CheckBox Style="{StaticResource chkMfsstyle}"/>
</StackPanel>
Output
In this case you can use the EventTrigger:
Represents a trigger that applies a set of actions in response to an event.
Example:
<StackPanel>
<StackPanel.Triggers>
<EventTrigger RoutedEvent="CheckBox.Checked" SourceName="First">
<BeginStoryboard>
<Storyboard>
<BooleanAnimationUsingKeyFrames Storyboard.TargetName="Second"
Storyboard.TargetProperty="IsChecked">
<DiscreteBooleanKeyFrame KeyTime="00:00:00" Value="True" />
</BooleanAnimationUsingKeyFrames>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
<EventTrigger RoutedEvent="CheckBox.Checked" SourceName="Second">
<BeginStoryboard>
<Storyboard>
<BooleanAnimationUsingKeyFrames Storyboard.TargetName="First"
Storyboard.TargetProperty="IsChecked">
<DiscreteBooleanKeyFrame KeyTime="00:00:00" Value="True" />
</BooleanAnimationUsingKeyFrames>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
<EventTrigger RoutedEvent="CheckBox.Unchecked" SourceName="First">
<BeginStoryboard>
<Storyboard>
<BooleanAnimationUsingKeyFrames Storyboard.TargetName="Second"
Storyboard.TargetProperty="IsChecked">
<DiscreteBooleanKeyFrame KeyTime="00:00:00" Value="False" />
</BooleanAnimationUsingKeyFrames>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
<EventTrigger RoutedEvent="CheckBox.Unchecked" SourceName="Second">
<BeginStoryboard>
<Storyboard>
<BooleanAnimationUsingKeyFrames Storyboard.TargetName="First"
Storyboard.TargetProperty="IsChecked">
<DiscreteBooleanKeyFrame KeyTime="00:00:00" Value="False" />
</BooleanAnimationUsingKeyFrames>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</StackPanel.Triggers>
<CheckBox Name="First"
Content="First" />
<CheckBox Name="Second"
Content="Second" />
</StackPanel>
The Storyboard can be stored in Resources for readability, like so:
<Window.Resources>
<Storyboard x:Key="FirstCheckedStory" ... />
</Window.Resources>
And then use like this:
<EventTrigger RoutedEvent="CheckBox.Checked" SourceName="First">
<BeginStoryboard Storyboard="{StaticResource FirstCheckedStory}" />
</EventTrigger>
Also, the Storyboard can contain several actions, just put him in order:
<Storyboard>
<BooleanAnimationUsingKeyFrames ... />
<BooleanAnimationUsingKeyFrames ... />
</Storyboard>

Trouble using a DataTrigger with Rectangle in WPF

I'm new to WPF and am trying to animate the changes to a Rectangle's size and position. I have my rectangle being set by binding it to an INotifyPropertyChanged property. This works fine but the changes can be extreme and I'd like it to be visually less jarring.
This is working fine:
<Rectangle Fill="{x:Static SystemColors.ControlBrush}"
Stroke="black">
<Rectangle.Style>
<Style TargetType="{x:Type Rectangle}">
<Setter Property="Canvas.Left" Value="{Binding Path=Coil.Left, Mode=OneWay}"/>
<Setter Property="Canvas.Top" Value="{Binding Path=Coil.Top, Mode=OneWay}"/>
<Setter Property="Width" Value="{Binding Path=Coil.Width, Mode=OneWay}"/>
<Setter Property="Height" Value="{Binding Path=Coil.Height, Mode=OneWay}"/>
</Style>
</Rectangle.Style>
Coil.Left is a RectangleF structure within my INotifyPropertyChanged object and the DataContext property of my canvas containing the rectangle is being set in my code behind after the object is created.
I've also been able to animate this using an EventTrigger just fine:
<Rectangle Fill="{x:Static SystemColors.ControlBrush}"
Stroke="black">
<Rectangle.Style>
<Style TargetType="{x:Type Rectangle}">
<Setter Property="Canvas.Left" Value="{Binding Path=Coil.Left, Mode=OneTime}"/>
<Setter Property="Canvas.Top" Value="{Binding Path=Coil.Top, Mode=OneTime}"/>
<Setter Property="Width" Value="{Binding Path=Coil.Width, Mode=OneTime}"/>
<Setter Property="Height" Value="{Binding Path=Coil.Height, Mode=OneTime}"/>
</Style>
</Rectangle.Style>
<Rectangle.Triggers>
<EventTrigger RoutedEvent="Rectangle.MouseEnter">
<EventTrigger.Actions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="Width"
To="{Binding Path=Coil.Width, Mode=OneWay}"
Duration="0:0:5"/>
<DoubleAnimation Storyboard.TargetProperty="Height"
To="{Binding Path=Coil.Height, Mode=OneWay}"
Duration="0:0:0.5"/>
<DoubleAnimation Storyboard.TargetProperty="(Canvas.Left)"
To="{Binding Path=Coil.Left, Mode=OneWay}"
Duration="0:0:5"/>
<DoubleAnimation Storyboard.TargetProperty="(Canvas.Top)"
To="{Binding Path=Coil.Top, Mode=OneWay}"
Duration="0:0:5"/>
</Storyboard>
</BeginStoryboard>
</EventTrigger.Actions>
</EventTrigger>
</Rectangle.Triggers>
When I move my mouse over the Rectangle, it changes via animation to the current property values of Coil.Left, Top, Width, and Height as expected.
However, what I want to accomplish is for any change to the Coil property values to be an animated change. So my plan was to set a DataTrigger and use a converter to return always true so that any change would result in the changes being played out via animation.
But regardless of what I do, I'm getting an exception error before it even opens the window when I use a DataTrigger and I've got no idea why this is happening. To keep it simple, I took out the converter and just used a straight value:
<Rectangle Fill="{x:Static SystemColors.ControlBrush}"
Stroke="black">
<Rectangle.Style>
<Style TargetType="{x:Type Rectangle}">
<Setter Property="Canvas.Left" Value="{Binding Path=Coil.Left, Mode=OneTime}"/>
<Setter Property="Canvas.Top" Value="{Binding Path=Coil.Top, Mode=OneTime}"/>
<Setter Property="Width" Value="{Binding Path=Coil.Width, Mode=OneTime}"/>
<Setter Property="Height" Value="{Binding Path=Coil.Height, Mode=OneTime}"/>
</Style>
</Rectangle.Style>
<Rectangle.Triggers>
<DataTrigger Binding="{Binding Path=Coil.Height}" Value="60">
<DataTrigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="Width"
To="{Binding Path=Coil.Width, Mode=OneWay}"
Duration="0:0:5"/>
<DoubleAnimation Storyboard.TargetProperty="Height"
To="{Binding Path=Coil.Height, Mode=OneWay}"
Duration="0:0:0.5"/>
<DoubleAnimation Storyboard.TargetProperty="(Canvas.Left)"
To="{Binding Path=Coil.Left, Mode=OneWay}"
Duration="0:0:5"/>
<DoubleAnimation Storyboard.TargetProperty="(Canvas.Top)"
To="{Binding Path=Coil.Top, Mode=OneWay}"
Duration="0:0:5"/>
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
</DataTrigger>
</Rectangle.Triggers>
Any thoughts on why this would give me an exception error? I'm sure it's just a matter of my inexperience with WPF. This is my first project.
Thank you in advance.
--John
For a FrameworkElement like a Rectangle you can only use EventTrigger. In Style, ControlTemplate and DataTemplate you can use Trigger / MultiTrigger and DataTrigger / MultiDataTrigger too.
This means move your DataTrigger into Rectangle style and it should work:
<Rectangle Fill="{x:Static SystemColors.ControlBrush}" Stroke="Black">
<Rectangle.Style>
<Style TargetType="{x:Type Rectangle}">
...
<Style.Triggers>
<DataTrigger Binding="{Binding Path=Coil.Height}" Value="60">
...
</DataTrigger>
</Style.Triggers>
</Style>
</Rectangle.Style>
</Rectangle>
Was reading about this recently. Despite the similarity of EventTriggers and DataTriggers, you can use Binding inside the Storyboard for the EventTrigger but not the DataTrigger.
So you'll get a System.Windows.Markup.XamlParseException error if you try using binding inside the Storyboard condition such as below,
<Style.Triggers>
<DataTrigger Binding="{Binding Path=IsBarVisible,
Converter={StaticResource myBooleanToVisibiltyConverter}}" Value="Visible">
<DataTrigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation
Storyboard.TargetProperty="Value"
From="{Binding Path=ProgressedAmout}"
To="100"
Duration="0:0:50"
></DoubleAnimation>
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
</DataTrigger>
</Style.Triggers>
You can use and EventTrigger in return or get rid of the Storyborad's To to From binding.

How to delay the Setter to be in affect for 0.5 sec in DataTrigger?

I am wondering if it possible to delay a datatrigger to change layout for 0.5 a second. Is any easy way to do it? I need to set the visibility of the object but wait for 0.5 a second. Any adeas are highly appreciated.
<DataTemplate x:Key="ListBoxItemDataTemplate">
<Grid x:Name="DataItem">
<Image x:Name="IconImage" Source="{Binding XPath=#icon}" Height="16" Margin="16,0,0,0" Stretch="None" VerticalAlignment="Center" HorizontalAlignment="Left" />
<TextBlock x:Name="ListboxIemtextBlock" Text="{Binding XPath=#name}" />
<Image x:Name="ArrowImage" Height="10" Source="Resources/Images/arrow_collapsed_grey.png" Visibility="{Binding XPath=#state}"/>
</Grid>
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding IsSelected, RelativeSource={RelativeSource AncestorType={x:Type ListBoxItem}, Mode=FindAncestor}}" Value="True">
<Setter TargetName="ListboxIemtextBlock" Property="Foreground" Value="White"/>
<Setter TargetName="IconImage" Property="Source" Value="{Binding XPath=#iconSelected}"/>
<Setter TargetName="IconImage" Property="Height" Value="16"/>
<Setter TargetName="ArrowImage" Property="Source" Value="Resources/Images/arrow_collapsed_white.png"/>
</DataTrigger>
<DataTrigger Binding="{Binding IsMouseOver, RelativeSource={RelativeSource AncestorType={x:Type ListBoxItem}, Mode=FindAncestor}}" Value="True">
<Setter TargetName="ListboxIemtextBlock" Property="Foreground" Value="#FF6dacbe"/>
</DataTrigger>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource AncestorType={x:Type ListBox}}, Path=SelectedItem.Attributes[retract].Value}" Value="True">
<Setter TargetName="ListboxIemtextBlock" Property="Visibility" Value="Hidden" />
<DataTrigger.EnterActions>
<BeginStoryboard Name="StartAnimation" Storyboard="{StaticResource MakeObjectVisibleAfterHalfASecond}"/>
</DataTrigger.EnterActions>
<DataTrigger.ExitActions>
<RemoveStoryboard BeginStoryboardName="StartAnimation"/>
</DataTrigger.ExitActions>
</DataTrigger>
</DataTemplate.Triggers></DataTemplate>
Storyaboard:
<Storyboard x:Key="MakeObjectVisibleAfterHalfASecond" Storyboard.TargetName="ListboxIemtextBlock">
<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Visibility" Duration="0" BeginTime="0:0:.5">
<DiscreteObjectKeyFrame Value="{x:Static Visibility.Visible}" />
</ObjectAnimationUsingKeyFrames>
</Storyboard>
It can be done using an animation. The pieces involved are:
1) An ObjectAnimationUsingKeyFrames that sets the Visibility property on the target, with a BeginTime of 0:0:.5 to delay this for a half second when the storyboard begins.
2) A DataTrigger that checks the property whose change will make the object visible (in this case, the IsChecked property on the CheckBox named Start).
3) BeginStoryboard in DataTrigger.EnterActions that launches the animation, and a RemoveStoryboard in DataTrigger.ExitActions that makes the object invisible again if the bound property changes back.
Here's a simple working example:
<Page
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Page.Resources>
<Storyboard x:Key="MakeObjectVisibleAfterHalfASecond">
<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Visibility"
Duration="0"
BeginTime="0:0:.5">
<DiscreteObjectKeyFrame Value="{x:Static Visibility.Visible}" />
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</Page.Resources>
<DockPanel>
<CheckBox DockPanel.Dock="Top"
Margin="10"
x:Name="Start">Check this to make the label appear</CheckBox>
<Border BorderThickness="2"
BorderBrush="AliceBlue"
CornerRadius="5"
Margin="10"
Padding="10"
DockPanel.Dock="Top">
<Label Visibility="Hidden">
<Label.Content>This should appear a half second after the box is checked.</Label.Content>
<Label.Style>
<Style TargetType="Label">
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=Start, Path=IsChecked}" Value="True">
<DataTrigger.EnterActions>
<BeginStoryboard Name="StartAnimation"
Storyboard="{StaticResource MakeObjectVisibleAfterHalfASecond}"/>
</DataTrigger.EnterActions>
<DataTrigger.ExitActions>
<RemoveStoryboard BeginStoryboardName="StartAnimation"/>
</DataTrigger.ExitActions>
</DataTrigger>
</Style.Triggers>
</Style>
</Label.Style>
</Label>
</Border>
<TextBlock/>
</DockPanel>
</Page>
Note that you could also do this by omitting BeginTime and setting Duration on the animation, since the two are essentially the same thing with a key-frame animation.

Resources