【问题标题】:Dispose of ReactiveCommand after execution执行后处理 ReactiveCommand
【发布时间】:2017-05-15 13:17:18
【问题描述】:

我正在使用System.Windows.Interactivity 绑定LoadedCommand,如下所示:

<i:Interaction.Triggers>
    <i:EventTrigger EventName="Loaded">
        <i:InvokeCommandAction Command="{Binding LoadedCommand}" />
    </i:EventTrigger>
</i:Interaction.Triggers>

在我的视图模型中,我正在加载视图时加载数据:

LoadedCommand = ReactiveCommand.CreateFromTask(LoadData);

我的问题是 Loaded 事件实际上是由父视图多次触发的。我想在第一次执行后停止LoadedCommand,而不做类似的事情:

async Task LoadData()
{
    if (didLoad) return;
    ...
}

【问题讨论】:

    标签: c# wpf system.reactive reactiveui


    【解决方案1】:

    这是一个棘手的问题,因为 Load 处理程序被多次调用。这很烦人。所以我们有一些帮助代码来解决这个问题。

    using System;
    using System.Collections.Generic;
    using System.Reactive.Disposables;
    using System.Reactive.Linq;
    using System.Windows;
    
    namespace Utils
    {
        public static class FrameworkElementExtensions
        {
            public static void LoadUnloadHandler
                ( this FrameworkElement control
                , Func<IDisposable> action)
            {
                var state = false;
                var cleanup = new SerialDisposable();
                Observable.Merge
                    (Observable.Return(control.IsLoaded)
                        , control.Events().Loaded.Select(x => true)
                        , control.Events().Unloaded.Select(x => false)
                    )
                    .Subscribe(isLoadEvent =>
                    {
                        if (!state)
                        {
                            // unloaded state
                            if (isLoadEvent)
                            {
                                state = true;
                                cleanup.Disposable = 
                                    new CompositeDisposable(action());
                            }
                        }
                        else
                        {
                            // loaded state
                            if (!isLoadEvent)
                            {
                                state = false;
                                cleanup.Disposable = Disposable.Empty;
                            }
                        }
    
                    });
            }
    
    
        }
    }
    

    代码处理 LoadUnload 事件并过滤掉多次调用问题。这个想法是您提供一个回调,该回调在 Load 上执行某些操作并返回一个 IDisposable,该 IDisposable 将在 Unload 上处理。

    例如,如果您需要注册到外部资源(例如网络连接),但只有当控件可见时,您才能在构造函数后面的代码中进行。

    this.LoadUnloadHandler(()=>{
        MyViewModel.Connect();
        return Disposable.Create(()=>MyViewModel.Disconnect());
    });
    

    使用的类/库

    【讨论】:

    • 我试图避免在代码隐藏中编写代码。如果我要写在那里,我可以写Observable.FromEventPattern(this, LoadedEvent.Name).Take(1).Subscribe(async _ =&gt; await ViewModel.LoadData());吗? Take(1) 应该处理掉我的 Observable,对吗?
    • 如果您只希望它在加载时发生一次并且可能满足您的确切需要,那么这将起作用。我的代码将在每次实际加载时正确运行,跳过双重调用并在卸载时处理任何资源。我一直都在使用它,尤其是从我需要清理的代码中注册到全局资源时。然而,FromEventPattern 相当丑陋。我建议像我所做的那样使用 ReactiveUI.Events ,但这更像是一个品味问题,而不是关键。也不要回避后面的代码。尝试在 XAML 中做一些事情简直是天方夜谭。
    • 太棒了。感谢您的洞察力!
    【解决方案2】:

    在 LoadData 方法中将命令设置为 null。所以你不需要为此维护一个单独的标志。

    async Task LoadData()
    {
        // your logic goes here
        LoadedCommand = null;
    }
    

    【讨论】:

    • 我试过了,但 LoadData 方法仍然被调用。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-07-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-03-10
    • 1970-01-01
    相关资源
    最近更新 更多