Отложенная отправка?
В WPF из-за тонкостей, связанных с обновлением интерфейса, мне иногда приходится выполнять действия после короткой задержки.
В настоящее время я делаю это просто:
var dt = new DispatcherTimer(DispatcherPriority.Send);
dt.Tick += (s, e) =>
{
dt.Stop();
//DoStuff
};
dt.Interval = TimeSpan.FromMilliseconds(200);
dt.Start();
Но это и немного уродливое и, возможно, слишком много накладных расходов, чтобы каждый раз создавать новый таймер (?)
Что лучше всего с точки зрения производительности, чтобы сделать это, т.е. выполнить наиболее оперативно? И какой хороший способ переписать вышеуказанный код во что-то вроде:
this.Dispatcher.BeginInvoke(new Action(delegate()
{
//DoStuff
}), DispatcherPriority.Send,TimeSpan.FromMilliseconds(200));
Где Timespan - это задержка, спасибо за любой вход:)
Ответы
Ответ 1
Я бы не предположил, что DispatcherTimer
тяжелый... почему бы просто не написать метод расширения на Dispatcher
, который позволяет вам использовать синтаксис, который вы хотите, и использует DispatcherTimer
в фоновом режиме? Я бы лично назвал его DelayInvoke
, а не BeginInvoke
, хотя... и я также исправлю его, чтобы всегда использовать Action
, а не произвольный делегат..., что упростит использование лямбда-выражений:
Dispatcher.DelayInvoke(TimeSpan.FromMilliseconds(200), () => { ...
});
(Я склонен считать его более читаемым, если анонимные функции используются в качестве последнего аргумента в вызове метода, но это только личное предпочтение.)
Учитывая, что вы, возможно, захотите использовать миллисекунды большую часть времени, у вас может быть и другой вспомогательный метод:
Dispatcher.DelayInvokeMillis(200, () => { ...
});
Другой альтернативой попыткам является просто использовать существующий метод BeginInvoke
, но с очень низким приоритетом, поэтому ваш делегат будет вызван только после завершения всего остального. Не зная подробностей вашей ситуации, трудно понять, будет ли это работать или нет, но стоит попробовать.
Ответ 2
Способ .NET 4.5:
public async void MyMethod()
{
await Task.Delay(20);
await Dispatcher.BeginInvoke((Action)DoStuff);
}
Ответ 3
Возможно, вы можете использовать класс Paul Stowell DelayBinding: http://www.paulstovell.com/wpf-delaybinding.
С помощью этого can может привязываться к DependencyProperty для класса, который может выполнить действие при изменении свойства. Связывание будет управлять задержкой.
Может показаться особенно приятным, если у вас есть конструкция типа MVVM.
Ответ 4
Я не люблю DispatcherTimer, потому что нет простого способа передать параметры, кроме как через область/закрытие функций.
Вместо этого я использую Task.Delay()
и переношу это в метод расширения Dispatcher.Delay()
:
public static class DispatcherExtensions
{
public static void Delay(this Dispatcher disp, int delayMs,
Action<object> action, object parm = null)
{
var ignore = Task.Delay(delayMs).ContinueWith((t) =>
{
disp.Invoke(action, parm);
});
}
public static void DelayWithPriority(this Dispatcher disp, int delayMs,
Action<object> action, object parm = null,
DispatcherPriority priority = DispatcherPriority.ApplicationIdle)
{
var ignore = Task.Delay(delayMs).ContinueWith((t) =>
{
disp.BeginInvoke(action, priority, parm);
});
}
public static async Task DelayAsync(this Dispatcher disp, int delayMs,
Action<object> action, object parm = null,
DispatcherPriority priority = DispatcherPriority.ApplicationIdle)
{
await Task.Delay(delayMs);
await disp.BeginInvoke(action, priority, parm);
}
}
Чтобы вызвать это, выполните следующие действия:
Dispatcher.Delay(4000, (win) =>
{
var window = win as MainWindow;
window.ShowStatus(null, 0);
},this);