Reputation: 334
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
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
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