Комментарии от @Servy и @Enigmativity помогли мне определить это.Для тех, кто заинтересован, вот решение, которое я придумал.Любые предложения по моему подходу дают мне знать.
Я создал статический вспомогательный класс с именем WaitableEventHelper, который включает в себя следующую функцию.
public static Task WaitableDebouncer(
this Control c,
Action<EventHandler> addHandler,
Action<EventHandler> removeHandler,
IScheduler scheduler,
CancellationToken cancelToken,
TimeSpan limit,
Func<Task> func)
{
var mycts = new CancellationTokenSource();
bool activated = false;
bool active = false;
Func<Task> pending = null;
var awaiter = Observable.FromEventPattern(addHandler, removeHandler, scheduler)
.TakeUntil(x => { return cancelToken.IsCancellationRequested; })
.Do((x) => { activated = true; })
.Do((x) =>
{
//sets pending task to last in sequence
pending = func;
})
.Throttle(limit, scheduler)
.Do((x) => { active = true; }) //done with throttle
.ForEachAsync(async (x) =>
{
//get func
var f = pending;
//remove from list
pending = null;
//execute it
await f();
//have we been cancelled?
if (cancelToken.IsCancellationRequested)
{
mycts.Cancel();
}
//not active
active = false;
}, mycts.Token);
//if cancelled
cancelToken.Register(() =>
{
//never activated, force cancel
if (!activated)
{
mycts.Cancel();
}
//activated in the past but not currently active
if (activated && !active)
{
mycts.Cancel();
}
});
//return new awaiter based on conditions
return Task.Run(async () =>
{
try
{
//until awaiter finishes or is cancelled, this will block
await awaiter;
}
catch (Exception)
{
//cancelled, don't care
}
//if pending isn't null, that means we terminated before ForEachAsync reached it
//execute it
if (pending != null)
{
await pending();
}
});
}
Затем я использую его следующим образом.Вот пример с нажатием кнопки, b1 - объект System.Windows.Forms.Button.Это может быть что угодно.Для моего тестового приложения я менял цвета на некоторых панелях в главной форме.В соответствии с предыдущим кодом в OP задачи - это просто список типа Task.
var awaiter1 = b1.WaitableDebouncer(h => b1.Click += h, h => b1.Click -= h,
scheduler,
canceller.Token,
TimeSpan.FromMilliseconds(5000),
async () =>
{
Invoke(new Action(() =>
{
if (p1.BackColor == Color.Red)
{
p1.BackColor = Color.Orange;
}
else if (p1.BackColor == Color.Orange)
{
p1.BackColor = Color.Yellow;
}
else if (p1.BackColor == Color.Yellow)
{
p1.BackColor = Color.HotPink;
}
else
{
p1.BackColor = Color.Red;
}
}));
});
tasks.Add(awaiter1);
Еще один для TextChanged для текстового поля.t1 - это System.Windows.Forms.TextBox.Опять же, это может быть что угодно, я просто устанавливаю статическую строковую переменную someValue и обновляю метку в пользовательском интерфейсе.
var awaiter2 = t1.WaitableDebouncer(h => t1.TextChanged += h, h => t1.TextChanged -= h,
scheduler,
canceller.Token,
TimeSpan.FromMilliseconds(5000),
async () =>
{
savedValue = t1.Text;
Invoke(new Action(() => l1.Text = savedValue));
});
tasks.Add(awaiter2);
Тогда вот как выглядит завершение или завершение работы.Это может быть закрытие приложения или закрытие файла.Просто какое-то событие, когда нам нужно отменить привязку этих событий, но сохранить любую ожидающую работу, которая была инициирована пользователем, прежде чем сделать это.Представьте, что пользователь вводит текстовое поле, а затем быстро нажимает X, чтобы закрыть приложение.5 секунд еще не закончились.
private async Task AwaitPendingEvents()
{
if (tasks.Count > 0)
{
await Task.WhenAll(tasks);
}
}
У нас есть программа ожидания для всего приложения.При закрытии мы делаем.
//main cancel signal
canceller.Cancel();
await AwaitPendingEvents();
Пока что с моими тестами это работает.Если событие не было сгенерировано, оно будет отменено.Если событие было сгенерировано, мы затем посмотрим, есть ли еще незавершенная работа, которая еще не прошла через газ.Если это так, мы отменяем наблюдаемую и выполняем эту ожидающую работу самостоятельно, поэтому нам не нужно ждать по таймеруЕсли есть ожидающая работа, и мы уже прошли ее через дроссель, то мы просто ждем и даем наблюдаемой подписке закончить ее выполнение.Затем подписка отменяется после выполнения, если запрос отменен.