如何在从可视化树中删除元素之前执行一些代码?

问题描述 投票:0回答:2

我正在尝试为 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>

根据我的测试,到目前为止,我找不到任何好的解决方案,尽管我愿意接受导致类似行为的建议。

wpf events user-controls fadeout
2个回答
0
投票
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;
}



0
投票

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 的动画。
    

© www.soinside.com 2019 - 2024. All rights reserved.