我正在尝试为 WPF 中的用户控件实现一些淡入和淡出动画。对于淡入动画,我可以使用 Loaded 事件来完成此操作。
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);
}
}
我尝试使用
Unloaded
事件来实现淡出效果,却发现该事件是在 UserControl
从可视化树中删除之后(当 UserControl 不再可见或不可访问时)触发的。有没有办法在 UserControl“关闭”之前运行一些代码,例如 OnClosing
的 Window
事件?编辑:为了了解更多上下文,
UserControl
充当更复杂窗口的组件。只要属性
NowPlayingViewModel
不为空,它就会被激活,当为空时,它就会被停用(我这样做是为了隐藏用户控件)。当我将 ViewModel 设置为 null 时,我想要运行淡出动画,并且我希望保持代码隐藏与其他 ViewModel 逻辑分离。<!-- 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>
根据我的测试,到目前为止,我找不到任何好的解决方案,尽管我愿意接受导致类似行为的建议。
Closing
事件..但是您可以在加载 UserControl 时获取父窗口并在那里实现淡出行为..
首先,删除Unloaded += NowPlayingView_Unloaded;
然后,稍微修改一下Loaded
代码..
private Window ParentWindow
{
get
{
DependencyObject parentDepObj = this;
do
{
parentDepObj = VisualTreeHelper.GetParent(parentDepObj);
if (parentDepObj is Window parentWindow) return parentWindow;
} while (parentDepObj != null);
return null;
}
}
private void NowPlayingView_Loaded(object sender, RoutedEventArgs e)
{
DoubleAnimation animation = new(0.0, 1.0, AnimationDuration);
BeginAnimation(OpacityProperty, animation);
var parentWindow = this.ParentOfType<Window>();
parentWindow.Closing += WindowClosing;
}
private void WindowClosing(object sender, CancelEventArgs args)
{
var pw = ParentWindow;
pw.Closing -= WindowClosing;
args.Cancel = true;
var anim = new(1.0, 0.0, AnimationDuration);
anim.Completed += (s, _) => pw.Close();
BeginAnimation(OpacityProperty, anim);
}
。您可以通过简单的调用来替换 ParentWindow
属性的 getter
private Window ParentWindow => this.ParentOfType<Window>();
其中
ParentOfType
是一些公共静态类Utilities中的扩展函数..
public static T ParentOfType<T>(this DependencyObject child) where T : DependencyObject
{
var parentDepObj = child;
do
{
parentDepObj = VisualTreeHelper.GetParent(parentDepObj);
if (parentDepObj is T parent) return parent;
} while (parentDepObj != null);
return null;
}
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();
}
}
我创建了一个依赖属性
ShowWindow
。每当该属性更改为
true
时,都会触发 Storyboard 进行淡入动画。对于淡出,我只是连接了一个按钮 OnClick 事件,但我可以在 DependencyPropertyChanged 方法中轻松执行此操作。通过这种方式,我可以使代码保持一定程度的解耦,并通过绑定到 ShowWindow
上的
UserControl
依赖属性的属性直接从其 ViewModel 控制 UserControl 的动画。