I'm playing with async/await, but I have yet to discover a standard method to safely cancel an intensive task. I have tested the following, and it works exactly as intended, though I remain unsure if a standard method exists.
abstract class PageLoader<TPage, TElement> where TPage : Page
{
private TPage page;
private Task loader;
private volatile bool mode;
private volatile int token;
public PageLoader(TPage page)
{
if (page == null)
{
throw new ArgumentNullException("page");
}
this.page = page;
this.mode = true;
}
protected TPage Page => page;
protected bool Mode => mode;
public void Load(TElement[] items)
{
if (!page.Dispatcher.HasThreadAccess)
{
throw new InvalidOperationException("The current thread is not authorized.");
}
if (loader != null)
{
throw new InvalidOperationException("The loader requires reset.");
}
if (items == null)
{
throw new ArgumentNullException("items");
}
int value = token;
mode = true;
(loader = new Task(() => AutoLoad(value, items))).Start();
}
public async Task<bool> Set(bool retainMode)
{
if (!page.Dispatcher.HasThreadAccess)
{
throw new InvalidOperationException("The current thread is not authorized.");
}
if (mode)
{
mode = false;
if (loader != null)
{
await loader;
loader = null;
token++;
}
if (retainMode)
{
mode = true;
}
return true;
}
return false;
}
protected void Commit(Entry entry)
{
if (entry == null)
{
throw new ArgumentNullException("entry");
}
DispatchedHandler handler = () =>
{
if (entry.Token == token)
{
AutoCommit(entry);
}
};
var action = page.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, handler);
}
protected abstract void AutoLoad(int token, TElement[] items);
protected abstract void AutoCommit(Entry entry);
protected class Entry
{
public readonly int Token;
public readonly TElement Element;
public readonly object Data;
public Entry(int token, TElement element, object data)
{
Token = token;
Element = element;
Data = data;
}
}
}
Tested with:
class GridViewLoader : PageLoader<MainPage, int>
{
public GridViewLoader(MainPage page) : base(page) { }
protected override void AutoLoad(int token, int[] items)
{
foreach (int item in items)
{
if (!Mode)
{
break;
}
// (Intensive process per item..)
Commit(new Entry(token, item, null));
}
}
protected override void AutoCommit(Entry entry)
{
GridViewItem item = new GridViewItem()
{
Content = entry.Element.ToString()
};
Page.gridView.Items.Add(item);
}
}
Usage:
private GridViewLoader gridViewLoader;
private async void queryBox_QuerySubmitted(AutoSuggestBox sender, AutoSuggestBoxQuerySubmittedEventArgs args)
{
if (!await gridViewLoader.Set(false))
{
return;
}
gridView.Items.Clear();
int[] selection;
// (Do stuff..)
selection = new int[] { 1, 2, 3 };
if (selection != null)
{
gridViewLoader.Load(selection);
}
}