开发者

Delayed Dispatch Invoke?

In WPF due to the intricacies on how the interface is updated I sometimes have to perform actions after a short delay.

Currently I'm doing this simply by:

        var dt = new DispatcherTimer(DispatcherPriority.Send);
        dt.Tick += (s, e) =>
        {
            dt.Stop();
            //DoStuff
        };
        dt.Interval = TimeSpan.FromMilliseconds(200);
        dt.Start();

But it's both a bit ugly and perhaps too mu开发者_运维问答ch overhead to create a new timer each time (?) What's the best from a performance standpoint to do it, ie execute most promptly? And what's good way to rewrite the above code into something like:

        this.Dispatcher.BeginInvoke(new Action(delegate()
        {
            //DoStuff
        }), DispatcherPriority.Send,TimeSpan.FromMilliseconds(200));

Where Timespan is the delay, Thanks for any input :)


I wouldn't assume that the DispatcherTimer is heavy-weight... why not just write an extension method on Dispatcher which allows you to use the syntax you want, and uses a DispatcherTimer in the background? I would personally call it DelayInvoke rather than BeginInvoke though... and I'd also fix it to always use Action rather than an arbitrary delegate... that will make it easier to use lambda expressions:

Dispatcher.DelayInvoke(TimeSpan.FromMilliseconds(200), () => { ... 
});

(I tend to find it's more readable if anonymous functions are used as the final argument in a method call, but that's just a personal preference.)

Given that you'll quite possibly want to use milliseconds most of the time, you could have another helper method too:

Dispatcher.DelayInvokeMillis(200, () => { ... 
});

Another alternative to try is merely to use the existing BeginInvoke method but with a very low priority, so your delegate will only be invoked after everything else has finished. Without knowing the details of your situation, it's hard to know whether that'll work or not - but it's worth trying.


A .NET 4.5 way:

    public async void MyMethod()
    {
        await Task.Delay(20); 
        await Dispatcher.BeginInvoke((Action)DoStuff);
    }


I'm not fond of the DispatcherTimer because there is no easy way to pass parameters in other than through function scope/closures.

Instead I use a Task.Delay() and wrap that into a Dispatcher.Delay() extension method:

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

To call this then:

Dispatcher.Delay(4000, (win) =>
{
     var window = win as MainWindow;
     window.ShowStatus(null, 0);
},this);


You might be able to use the Paul Stowell's DelayBinding class from here: http://www.paulstovell.com/wpf-delaybinding.

With this can can bind to a DependencyProperty on a class that can execute the action when the property changes. The binding will manage the delay. Might be particularly nice if you have a MVVM-type design.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜