DobleA
DobleA

Reputation: 334

Dispose of ReactiveCommand after execution

I'm binding a LoadedCommand using System.Windows.Interactivity like so:

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

And in my view-model I am loading data when the view is loaded:

LoadedCommand = ReactiveCommand.CreateFromTask(LoadData);

My problem is that the Loaded event is actually triggered multiple times by the parent view. I would like to stop the LoadedCommand after its first execution without doing something like:

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

Upvotes: 2

Views: 1206

Answers (2)

bradgonesurfing
bradgonesurfing

Reputation: 32202

This is a tricky one because the Load handler is called multiple times. It's annoying. So we have some helper code to get around this.

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;
                        }
                    }

                });
        }


    }
}

The code handles the Load and Unload event and filters out the multiple call problem. The idea is that you provide a callback that does something on Load and returns an IDisposable that will be disposed on Unload.

For example if you need to register to an outside resource such as a network connection but only when the control is visible you could do in the code behind constructor.

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

Classes / Libraries used

Upvotes: 1

Parag
Parag

Reputation: 543

Set command to null in LoadData method. So you don't need to maintain a separate flag for this.

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

Upvotes: 1

Related Questions