I am trying to implement some fade-in and fade-out animations for a user control in WPF. For the fade-in animation I was able to use the Loaded event to accomplish that.
public sealed partial class NowPlayingView : UserControl
{
public Duration AnimationDuration
{
get { return (Duration)GetValue(AnimationDurationProperty); }
set { SetValue(AnimationDurationProperty, value); }
}
public static readonly DependencyProperty AnimationDurationProperty =
DependencyProperty.Register("AnimationDuration", typeof(Duration), typeof(NowPlayingView), new PropertyMetadata(Duration.Automatic));
public NowPlayingView()
{
Opacity = 0;
InitializeComponent();
Loaded += NowPlayingView_Loaded;
Unloaded += NowPlayingView_Unloaded;
}
private void NowPlayingView_Unloaded(object sender, RoutedEventArgs e)
{
DoubleAnimation animation = new(1.0, 0.0, AnimationDuration);
BeginAnimation(OpacityProperty, animation);
}
private void NowPlayingView_Loaded(object sender, RoutedEventArgs e)
{
DoubleAnimation animation = new (0.0, 1.0, AnimationDuration);
BeginAnimation(OpacityProperty, animation);
}
}
I attempted to use the Unloaded
event for the fade-out effect only to find out that the event is fired after the UserControl
is removed from the visual tree (when the UserControl is no longer visible or accessible). Is there a way to run some code right before the UserControl "closes", something like the OnClosing
event of a Window
?
EDIT:
For a bit more context, the UserControl
acts as a component of a more complex window. It is activated whenever the Property NowPlayingViewModel
is not null and deactivated when null (which I do in order to hide the UserControl). It is when I set the ViewModel to null that I want to run the fade-out animation and I would like to keep the code-behind decoupled from other ViewModel logic.
<!-- Now playing View-->
<ContentControl Grid.RowSpan="3" Grid.ColumnSpan="2" Content="{Binding NowPlayingViewModel}">
<ContentControl.Resources>
<DataTemplate DataType="{x:Type viewmodels:NowPlayingViewModel}">
<views:NowPlayingView AnimationDuration="00:00:00.8" />
</DataTemplate>
</ContentControl.Resources>
</ContentControl>
From my testing, I couldn't find any good solution to this so far, though I am open to suggestions that lead to similar behavior.
I found a simpler and more elegant solution to this problem.
public sealed partial class NowPlayingView : UserControl
{
private bool _animating = false;
public bool ShowWindow
{
get { return (bool)GetValue(ShowWindowProperty); }
set { SetValue(ShowWindowProperty, value); }
}
public static readonly DependencyProperty ShowWindowProperty =
DependencyProperty.Register("ShowWindow", typeof(bool), typeof(NowPlayingView), new PropertyMetadata(false, OnShowWindowChanged));
private static void OnShowWindowChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (d is NowPlayingView userControl && (bool)e.NewValue == true)
userControl.Show();
}
public NowPlayingView()
{
InitializeComponent();
Opacity = 0;
}
private void Button_Click(object sender, RoutedEventArgs e)
{
_animating = true;
Hide();
_animating = false;
}
private void Show()
{
Visibility = Visibility.Visible;
DoubleAnimation animation = new()
{
From = 0.0,
To = 1.0,
FillBehavior = FillBehavior.HoldEnd,
Duration = new(TimeSpan.FromMilliseconds(600))
};
Storyboard storyboard = new();
storyboard.Children.Add(animation);
Storyboard.SetTarget(animation, this);
Storyboard.SetTargetProperty(animation, new PropertyPath(OpacityProperty));
storyboard.Begin();
}
private void Hide()
{
DoubleAnimation animation = new()
{
From = 1.0,
To = 0.0,
FillBehavior = FillBehavior.Stop,
Duration = new(TimeSpan.FromMilliseconds(600))
};
Storyboard storyboard = new();
storyboard.Children.Add(animation);
Storyboard.SetTarget(animation, this);
Storyboard.SetTargetProperty(animation, new PropertyPath(OpacityProperty));
storyboard.Completed += delegate
{
Visibility = Visibility.Collapsed;
};
storyboard.Begin();
}
}
I created a dependency property ShowWindow
. Whenever the property is changed to true
, it will trigger the Storyboard for the fade-in animation. For fade-out I just hooked up a button OnClick event, but I could just as easily do this in the DependencyPropertyChanged method.
This way I keep the code somewhat decoupled and control the animation of the UserControl directly from its ViewModel with a property binded to the ShowWindow
dependency property on the UserControl
.