I want a control (e.g. a GroupBox) to show a grow animation when it becomes visible and a shrink animation, when the visibility is changed to "Collapsed".
Therefore, I created a style which implements an animated grow and shrink effect as shown here in a small sample application (shown below).
However, only the grow animation is shown. Instead of showing the shrink animation, the groupbox disappears at once.
Can anyone tell me, why?
And even better, how to fix it?
<Window x:Class="ShrinkTest.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<Style TargetType="FrameworkElement" x:Key="ExpandableElement">
<Setter Property="RenderTransformOrigin" Value="0.5 0" />
<Setter Property="RenderTransform">
<Setter.Value>
<ScaleTransform/>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="Visibility" Value="Visible">
<Trigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="RenderTransform.ScaleY" From="0" To="1" Duration="0:0:0.5" AccelerationRatio="0.2" DecelerationRatio="0.4"/>
</Storyboard>
</BeginStoryboard>
</Trigger.EnterActions>
</Trigger>
<Trigger Property="Visibility" Value="Hidden">
<Trigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="RenderTransform.ScaleY" From="1" To="0" Duration="0:0:0.5" AccelerationRatio="0.2" DecelerationRatio="0.4"/>
</Storyboard>
</BeginStoryboard>
</Trigger.EnterActions>
</Trigger>
</Style.Triggers>
</Style>
</Window.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Button Grid.Row="0" Margin="8" Width="140" Click="ButtonBase_OnClick">Expand / Shrink</Button>
<TextBlock Grid.Row="1" Text="--- Header ---"/>
<GroupBox x:Name="GroupBox" Grid.Row="2" Header="GroupBox" Style="{StaticResource ExpandableElement}" >
<StackPanel Orientation="Vertical">
<TextBlock Text="Test Test Test"/>
<TextBlock Text="Test Test Test"/>
<TextBlock Text="Test Test Test"/>
<TextBlock Text="Test Test Test"/>
<TextBlock Text="Test Test Test"/>
</StackPanel>
</GroupBox>
<TextBlock Grid.Row="3" Text="--- Footer ---"/>
</Grid>
</Window>
I had a similar problem. Just think about it for a minute... your problem is that you can see your animation when it's visible, but you can't when it is hidden. That is also your answer to why... because it is hidden. I know, that's fairly unsatisfactory answer, but that's just how it is.
As to how to fix it... well saying it is simple, but implementing it is not. Simply put, you have to run your animation until it ends and then set the Visibility to Hidden. So unfortunately this means that nice, simple setting the Visibility property in the Trigger is no longer viable... it's ok to make it visible, just not for hiding.
In my case, I have a whole framework that I built my animations into. Basically speaking though, when I remove items from the collections, internally the item is not actually removed, but instead its exit animation is started. Only when that animation is complete will the internal collection actually remove the item.
So if you can be bothered, then you'll have to implement something like this where, rather than setting the Visibility property to Hidden, you set another property to true which triggers the animation and when the Completed event from that animation is called, then you set the Visibility property to Hidden.
Sheridan is right. As soon as a control becomes invisible, it doesn't matter, which animation you apply to it. :-)
So I created a special ExpandingContentControl:
public class ExpandingContentControl : ContentControl
{
public static readonly DependencyProperty IsExpandedProperty = DependencyProperty.Register(
"IsExpanded", typeof(bool), typeof(ExpandingContentControl), new PropertyMetadata(false));
public bool IsExpanded
{
get { return (bool)GetValue(IsExpandedProperty); }
set { SetValue(IsExpandedProperty, value); }
}
public ExpandingContentControl()
{
Visibility = IsExpanded ? Visibility.Visible : Visibility.Collapsed;
}
}
But there was also a problem with the style: Creating two triggers which are bound to different values of the same property obviously doesn't work.
Instead, I'm now using just one trigger where the EnterAction implements growing and the ExitAction implements shrinking the control:
<Style TargetType="controls:ExpandingContentControl" >
<Setter Property="RenderTransformOrigin" Value="0.5 1" />
<Setter Property="RenderTransform">
<Setter.Value>
<ScaleTransform/>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="IsExpanded" Value="True">
<Trigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Visibility" >
<DiscreteObjectKeyFrame Value="{x:Static Visibility.Visible}" KeyTime="00:00:00"/>
</ObjectAnimationUsingKeyFrames>
<DoubleAnimation Storyboard.TargetProperty="RenderTransform.ScaleY" From="0" To="1"
Duration="0:0:0.3" DecelerationRatio="0.4"/>
</Storyboard>
</BeginStoryboard>
</Trigger.EnterActions>
<Trigger.ExitActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="RenderTransform.ScaleY" From="1" To="0" Duration="0:0:0.2" />
<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Visibility">
<DiscreteObjectKeyFrame Value="{x:Static Visibility.Collapsed}" KeyTime="00:00:0.2"/>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</BeginStoryboard>
</Trigger.ExitActions>
</Trigger>
</Style.Triggers>
</Style>
Related
In short my quetion is: Can I use databinding for the duration property of a storyboard?
My intention was to design a circular progressbar where i have a simple counter. Now based on the counter I wanted to change the duration of the animation. Is there any way I can achieve this by databinding or is there any other way.
I am using an Arc to create the progressbar effect as you can see below. When the DependencyProperty PauseAnimation becomes false, my animation starts. When it is set to true, it resets(I couldnt find a way to Pause the animation so I am resetting it to start from begining). The below code runs perfectly but I am having trouble setting the duration of the storyboard. I want to change that based on a property in my control. Can I achieve this?
<ed:Arc x:Name="AnimatingArc" ArcThickness="2" ArcThicknessUnit="Pixel" StartAngle="0" EndAngle="360" Fill="{DynamicResource ApplicationPrimaryColour}" HorizontalAlignment="Left" Height="25" Stretch="None" Stroke="Transparent" VerticalAlignment="Top" Width="25" Margin="8,47,0,0">
<ed:Arc.Style>
<Style TargetType="{x:Type ed:Arc}">
<Style.Triggers>
<DataTrigger Binding="{Binding PauseAnimation}" Value="false">
<DataTrigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="StartAngle" From="0" To="360" Duration="0:0:30" />
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
<DataTrigger.ExitActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="StartAngle" To="0" Duration="0:0:10" />
</Storyboard>
</BeginStoryboard>
</DataTrigger.ExitActions>
</DataTrigger>
</Style.Triggers>
</Style>
</ed:Arc.Style>
I have two stackpanels, where the second panel is extra information that can be slided down and shown when clicking on a button (like jQuerys slideDown effect). And afterwards be slided up, when clicking the button again.
I´ve never been fiddling with animations before, but have been doing some research. I´m still quite confused though, and cant figure out this simple problem.
When I only listen on the Visibility=Visible property, it works fine. But when I also want to slide the panel up, it behaves weird.
This is my XAML code:
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<StackPanel Grid.Row="0" Width="600" Orientation="Horizontal">
<TextBlock Style="{StaticResource Heading4}">Panel 1</TextBlock>
<Button Width="300" Margin="30,0,0,0" Click="Button_OnClick">Click to slide other panel down</Button>
</StackPanel>
<StackPanel Name="StackPanelShowHide" Grid.Row="1" Width="500" Orientation="Vertical" Background="Beige" Height="70">
<StackPanel.Style>
<Style TargetType="StackPanel">
<Style.Triggers>
<Trigger Property="Visibility" Value="Visible">
<Trigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="Height" From="0" To="70" Duration="0:0:1" />
</Storyboard>
</BeginStoryboard>
</Trigger.EnterActions>
</Trigger>
<Trigger Property="Visibility" Value="Collapsed">
<Trigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="Height" From="70" To="0" Duration="0:0:1" />
</Storyboard>
</BeginStoryboard>
</Trigger.EnterActions>
</Trigger>
</Style.Triggers>
</Style>
</StackPanel.Style>
<TextBlock Style="{StaticResource Heading4}">New panel</TextBlock>
</StackPanel>
</Grid>
And this is my Codebehind:
private void Button_OnClick(object Sender, RoutedEventArgs E) {
if (StackPanelShowHide.Visibility == Visibility.Collapsed) {
StackPanelShowHide.Visibility = Visibility.Visible;
} else {
StackPanelShowHide.Visibility = Visibility.Collapsed;
}
}
Really hope you can help :)
Kind regards,
Lars
I guess that instead of slide up animation on collapse it just instantly disappears. This is because you set Visibility to Collapsed, so there is nothing to display.
To fix this:
a) use MVVM: add some property to ViewModel and trigger on it. Modify property via ICommand.
b) Do not set Visibility, just start proper Storyboard in event handler.
I believe that your problem is that when the StackPanel has a Visibility value of Collapsed, it is removed from the UI. Therefore, even if the Animation were to occur, you would not see it.
i am using the MVVM design pattern and do not want much code in my code behind. coding in XAML and C#.
when a user saves a new record i would like "record saved" to appear in a text Block then fade away.
this is the sort of thing i would like to work:
<TextBlock Name="WorkflowCreated" Text="Record saved">
<TextBlock.Triggers>
<DataTrigger Binding="{Binding Path=NewWorkflowCreated}">
<DataTrigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation
Storyboard.TargetName="WorkflowCreated"
Storyboard.TargetProperty="(TextBlock.Opacity)"
From="1.0" To="0.0" Duration="0:0:3"/>
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
</DataTrigger>
</TextBlock.Triggers>
so when NewWorkflowCreated is changed in the viewmodel it would trigger the animation, unfortunately this does not work. i have also tried this:
<TextBlock Name="Message" Text="This is a test.">
<TextBlock.Triggers>
<EventTrigger RoutedEvent="TextBlock.Loaded">
<BeginStoryboard>
<Storyboard>
<DoubleAnimation
Storyboard.TargetName="Message"
Storyboard.TargetProperty="(TextBlock.Opacity)"
From="1.0" To="0.0" Duration="0:0:3"/>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</TextBlock.Triggers>
</TextBlock>
any help would be much appreciated. Maybe there is away that requires code in the View model?
You're using a DataTrigger which needs to be in a style.
<Window.DataContext>
<WpfApplication2:TestViewModel/>
</Window.DataContext>
<Grid>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<Grid.Resources>
<Style x:Key="textBoxStyle" TargetType="{x:Type TextBlock}">
<Style.Triggers>
<DataTrigger Binding="{Binding Path=NewWorkflowCreated}" Value="True">
<DataTrigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation
Storyboard.TargetProperty="(TextBlock.Opacity)"
From="1.0" To="0.0" Duration="0:0:3"/>
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
</DataTrigger>
</Style.Triggers>
</Style>
</Grid.Resources>
<TextBlock Name="WorkflowCreated" Style="{StaticResource textBoxStyle}" Text="Record saved" />
<Button Content="press me" Grid.Row="1" Click="Button_Click_1"/>
</Grid>
public class TestViewModel : INotifyPropertyChanged
{
private bool _newWorkflowCreated;
public bool NewWorkflowCreated
{
get { return _newWorkflowCreated; }
set {
_newWorkflowCreated = value;
PropertyChanged(this, new PropertyChangedEventArgs("NewWorkflowCreated"));
}
}
#region Implementation of INotifyPropertyChanged
public event PropertyChangedEventHandler PropertyChanged;
#endregion
}
This sort of UI-specific behavior should definitely be handled in the View, not the ViewModel
I would suggest looking into the TextChanged event, and see about kicking off the animation in there
Not my blog but I pretty much found what I was looking for here:
https://michaelscherf.wordpress.com/2009/02/23/how-to-trigger-an-animation-when-textblocks-text-is-changed-during-a-databinding/
I have a problem with removing the animation of a property in WPF. When the storyboards are started using a DataTrigger I cannot remove the animation from the property as one would in other cases. No matter what I try or where: the OrientationProperty is locked to the endvalue of the animation. You can see this in this example because you cannot rotate the ScatterViewItem after the storyboard has finished.
This is the XAML:
<s:SurfaceWindow x:Class="SurfaceApplication1.SurfaceWindow1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:s="http://schemas.microsoft.com/surface/2008"
x:Name="_this"
Title="SurfaceApplication1"
>
<s:SurfaceWindow.Resources>
<ImageBrush x:Key="WindowBackground" Stretch="None" Opacity="0.6" ImageSource="pack://application:,,,/Resources/WindowBackground.jpg"/>
<Storyboard x:Key="flipForward" Completed="FlipCompleted">
<DoubleAnimation By="180"
FillBehavior="HoldEnd"
Duration="0:0:0.5"
Storyboard.TargetProperty="(s:ScatterViewItem.Orientation)" />
</Storyboard>
<Storyboard x:Key="flipBackward" Completed="FlipCompleted">
<DoubleAnimation By="-180"
FillBehavior="HoldEnd"
Duration="0:0:0.5"
Storyboard.TargetProperty="(s:ScatterViewItem.Orientation)" />
</Storyboard>
</s:SurfaceWindow.Resources>
<Grid Background="{StaticResource WindowBackground}" >
<s:ScatterView>
<s:ScatterViewItem x:Name="_item" Orientation="0">
<s:ScatterViewItem.Style>
<Style TargetType="{x:Type s:ScatterViewItem}" BasedOn="{StaticResource {x:Type s:ScatterViewItem}}">
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=_button,Path=IsChecked}" Value="True">
<DataTrigger.EnterActions>
<BeginStoryboard>
<StaticResource ResourceKey="flipForward" />
</BeginStoryboard>
</DataTrigger.EnterActions>
<DataTrigger.ExitActions>
<BeginStoryboard>
<StaticResource ResourceKey="flipBackward" />
</BeginStoryboard>
</DataTrigger.ExitActions>
</DataTrigger>
</Style.Triggers>
</Style>
</s:ScatterViewItem.Style>
<StackPanel>
<s:SurfaceToggleButton Margin="20" x:Name="_button">Click Me!</s:SurfaceToggleButton>
</StackPanel>
</s:ScatterViewItem>
</s:ScatterView>
</Grid>
</s:SurfaceWindow>
And this is the relevant code behind:
private void FlipCompleted(object sender, EventArgs e)
{
_item.BeginAnimation(ScatterViewItem.OrientationProperty, null); // Doesn't work
((sender as ClockGroup).Timeline as Storyboard).Remove(_item); // Doesn't work either
((sender as ClockGroup).Timeline as Storyboard).Remove(); // Neither does this
}
Does anyone has a hint on how to remove the animation from the OrientationProperty?
Just like you adde a storyboard with BeginStoryboard you can also remove onve with RemoveStoryboard in XAML.
<DataTrigger.EnterActions>
<RemoveStoryboard BeginStoryboardName="flipBackward" />
</DataTrigger.EnterActions>
I am suprised however that BeginAnimation(ScatterViewItem.OrientationProperty, null); didn't do the trick. IMHO it should have ...
I simply want to open up the WPF Popup with a delay, sort of like a ToolTip.
How can I achieve this?
And, by the way, Popup.PopupAnimation = PopupAnimation.Fade ... fades in too quickly. I want at least half a second in there.
You can create a style to be applied to the Popup in the following way:
<Style x:Key="TooltipPopupStyle" TargetType="Popup">
<Style.Triggers>
<DataTrigger Binding="{Binding PlacementTarget.IsMouseOver, RelativeSource={RelativeSource Self}}" Value="True">
<DataTrigger.EnterActions>
<BeginStoryboard x:Name="OpenPopupStoryBoard" >
<Storyboard>
<BooleanAnimationUsingKeyFrames Storyboard.TargetProperty="IsOpen" FillBehavior="HoldEnd">
<DiscreteBooleanKeyFrame KeyTime="0:0:0.25" Value="True"/>
</BooleanAnimationUsingKeyFrames>
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
<DataTrigger.ExitActions>
<PauseStoryboard BeginStoryboardName="OpenPopupStoryBoard"/>
<BeginStoryboard x:Name="ClosePopupStoryBoard">
<Storyboard>
<BooleanAnimationUsingKeyFrames Storyboard.TargetProperty="IsOpen" FillBehavior="HoldEnd">
<DiscreteBooleanKeyFrame KeyTime="0:0:1" Value="False"/>
</BooleanAnimationUsingKeyFrames>
</Storyboard>
</BeginStoryboard>
</DataTrigger.ExitActions>
</DataTrigger>
<Trigger Property="IsMouseOver" Value="True">
<Trigger.EnterActions>
<PauseStoryboard BeginStoryboardName="ClosePopupStoryBoard" />
</Trigger.EnterActions>
<Trigger.ExitActions>
<PauseStoryboard BeginStoryboardName="OpenPopupStoryBoard"/>
<ResumeStoryboard BeginStoryboardName="ClosePopupStoryBoard" />
</Trigger.ExitActions>
</Trigger>
</Style.Triggers>
</Style>
Then, whenever you want to use it, you would write markup similar to this (notice the binding for the PlacementTarget):
<TextBlock x:Name="TargetControl" Text="Hover over me!" />
<Popup PlacementTarget="{Binding ElementName=TargetControl}" Style="{StaticResource TooltipPopupStyle}">
<Border BorderBrush="Red" BorderThickness="1" Background="White">
<TextBlock Text="This is a Popup behaving somewhat like the tooltip!" Margin="10" />
</Border>
</Popup>
The answer cplotts pasted is good but may not apply in your case because it leaves the animation attached to the IsOpen property, effectively locking it in place and preventing it from being changed via direct property setting, binding, and other ways. This may make it difficult to use with your code, depending on how you are using it.
If that is the case, I would switch to starting a DispatcherTimer when you want to open a popup after some delay, like this:
_popupTimer = new DispatcherTimer(DispatcherPriority.Normal);
_popupTimer.Interval = TimeSpan.FromMilliseconds(100);
_popupTimer.Tick += (obj, e) =>
{
_popup.IsOpen = true;
};
_popupTimer.Start();
For a ToolTip-like behavior this could be done on MouseEnter. If you want to cancel the popup opening for some reason (such as if the mouse leaves the control before the popup appears), just:
_popupTimer.Stop();
Update
As cplotts obseved in the comment, you will also want to set _popup.IsOpen = false in some situations in the MouseLeave event, depending on your logic for handling the mouse enter / exit events between your control and the popup. Be aware that you usually don't want to blindly set IsOpen=false on every MouseLeave event, because it may do so when the popup appears over it. This would in some situations lead to a flickering popup. So you'll need some logic there.
First off ... the credit for this answer goes to Eric Burke. He answered this very question posted in the WPF Disciples group. I thought it would be useful to put this answer out on StackOverflow too.
Basically, you need to animate the IsOpen property of the Popup with with a DiscreteBooleanKeyFrame.
Check out the following xaml (which can easily be pasted into Kaxaml or another loose xaml editing utility):
<Page
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
>
<ContentPresenter>
<ContentPresenter.ContentTemplate>
<DataTemplate>
<Grid>
<CheckBox
x:Name="cb"
Width="100"
Height="40"
Content="Hover Over Me"
/>
<Popup
x:Name="popup"
Placement="Bottom"
PlacementTarget="{Binding ElementName=cb}"
>
<Border Width="400" Height="400" Background="Red"/>
</Popup>
</Grid>
<DataTemplate.Triggers>
<Trigger SourceName="cb" Property="IsMouseOver" Value="True">
<Trigger.EnterActions>
<BeginStoryboard x:Name="bsb">
<Storyboard>
<BooleanAnimationUsingKeyFrames
Storyboard.TargetName="popup"
Storyboard.TargetProperty="IsOpen"
FillBehavior="HoldEnd"
>
<DiscreteBooleanKeyFrame KeyTime="0:0:0.5" Value="True"/>
</BooleanAnimationUsingKeyFrames>
</Storyboard>
</BeginStoryboard>
</Trigger.EnterActions>
<Trigger.ExitActions>
<StopStoryboard BeginStoryboardName="bsb"/>
</Trigger.ExitActions>
</Trigger>
</DataTemplate.Triggers>
</DataTemplate>
</ContentPresenter.ContentTemplate>
</ContentPresenter>
</Page>
Please note that I modified his original solution slightly ... to trigger the IsOpen on mouse over versus checking the CheckBox as he had it. All in the attempt to make Popup behave a little like ToolTip.
System.Windows.Controls.ToolTip tp = new System.Windows.Controls.ToolTip();
System.Windows.Threading.DispatcherTimer tooltipTimer =
new System.Windows.Threading.DispatcherTimer
(
System.Windows.Threading.DispatcherPriority.Normal
);
private void TooltipInvalidCharacter()
{
tp.Content =
"A flie name cannot contain any of the following character :" +
"\n" + "\t" + "\\ / : * ? \" < > |";
tooltipTimer.Interval = TimeSpan.FromSeconds(5);
tooltipTimer.Tick += new EventHandler(tooltipTimer_Tick);
tp.IsOpen = true;
tooltipTimer.Start();
}
void tooltipTimer_Tick(object sender, EventArgs e)
{
tp.IsOpen = false;
tooltipTimer.Stop();
}
You can extend the XAML for this solution so the popup stays open as long as the mouse is over it, then disappears automatically.
I modified the sample as follows:
Create a "ClosePopop" animation which sets IsOpen to False after 0.5 seconds. I made this a resource because it's used twice.
For the control's IsMouseOver trigger, add an ExitAction that starts the ClosePopup animation. This gives the user a chance to move the mouse over the popup before it closes. I named this animation"bxb"
Add a Trigger to the popup's IsMouseOver property. On mouseover, stop (but don't remove) the original "bxb" ClosePopup animation. This leaves the popup visible; removing the animation here will make the popup close.
On the popup's mouseout, start a new ClosePopup animation then remove the "bxb" animation. The last step is critical because otherwise the first, stopped "bxb" animation will keep the popup open.
This version turns the pop blue while the mouse is over it so you can see the sequence of events with Kaxaml.
<Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<DataTemplate x:Key="TooltipPopup">
<Grid>
<CheckBox
x:Name="cb"
Width="100"
Height="40"
Content="Hover Over Me"/>
<Popup
x:Name="popup"
Placement="Bottom"
PlacementTarget="{Binding ElementName=cb}">
<Border x:Name="border" Width="400" Height="400" Background="Red"/>
</Popup>
</Grid>
<DataTemplate.Resources>
<Storyboard x:Key="ClosePopup">
<BooleanAnimationUsingKeyFrames
Storyboard.TargetName="popup"
Storyboard.TargetProperty="IsOpen"
FillBehavior="Stop">
<DiscreteBooleanKeyFrame KeyTime="0:0:0.5" Value="False"/>
</BooleanAnimationUsingKeyFrames>
</Storyboard>
</DataTemplate.Resources>
<DataTemplate.Triggers>
<Trigger SourceName="cb" Property="IsMouseOver" Value="True">
<Trigger.EnterActions>
<BeginStoryboard x:Name="bsb" >
<Storyboard>
<BooleanAnimationUsingKeyFrames
Storyboard.TargetName="popup"
Storyboard.TargetProperty="IsOpen"
FillBehavior="HoldEnd">
<DiscreteBooleanKeyFrame KeyTime="0:0:0.5" Value="True"/>
</BooleanAnimationUsingKeyFrames>
</Storyboard>
</BeginStoryboard>
</Trigger.EnterActions>
<Trigger.ExitActions>
<StopStoryboard BeginStoryboardName="bsb"/>
<BeginStoryboard x:Name="bxb" Storyboard="{StaticResource ClosePopup}"/>
</Trigger.ExitActions>
</Trigger>
<Trigger SourceName="popup" Property="IsMouseOver" Value="True">
<Setter TargetName="border" Property="Background" Value="Blue"/>
<Trigger.EnterActions>
<StopStoryboard BeginStoryboardName="bxb"/>
</Trigger.EnterActions>
<Trigger.ExitActions>
<BeginStoryboard Storyboard="{StaticResource ClosePopup}"/>
<RemoveStoryboard BeginStoryboardName="bxb"/>
</Trigger.ExitActions>
</Trigger>
</DataTemplate.Triggers>
</DataTemplate>
</Page>