101

I have the following sample code that zooms each time a button is pressed:

XAML:

<Window x:Class="WpfApplication12.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525">

    <Canvas x:Name="myCanvas">

        <Canvas.LayoutTransform>
            <ScaleTransform x:Name="myScaleTransform" />
        </Canvas.LayoutTransform> 

        <Button Content="Button" 
                Name="myButton" 
                Canvas.Left="50" 
                Canvas.Top="50" 
                Click="myButton_Click" />
    </Canvas>
</Window>

*.cs

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
    }

    private void myButton_Click(object sender, RoutedEventArgs e)
    {
        Console.WriteLine("scale {0}, location: {1}", 
            myScaleTransform.ScaleX,
            myCanvas.PointToScreen(GetMyByttonLocation()));

        myScaleTransform.ScaleX =
            myScaleTransform.ScaleY =
            myScaleTransform.ScaleX + 1;
        
        Console.WriteLine("scale {0}, location: {1}",
            myScaleTransform.ScaleX,
            myCanvas.PointToScreen(GetMyByttonLocation()));
    }
    
    private Point GetMyByttonLocation()
    {
        return new Point(
            Canvas.GetLeft(myButton),
            Canvas.GetTop(myButton));
    }
}

the output is:

scale 1, location: 296;315
scale 2, location: 296;315

scale 2, location: 346;365
scale 3, location: 346;365

scale 3, location: 396;415
scale 4, location: 396;415

as you can see, there is a problem, that I thought solve by using Application.DoEvents(); but... it does not exist a priori in .NET 4.

What to do?

serhio
  • 28,010
  • 62
  • 221
  • 374
  • 8
    Threading? Application.DoEvents() was the poor man's substitute for writing properly multi-threaded applications and extremely poor practice in any event. – Colin Mackay Dec 21 '10 at 17:25
  • 5
    I know that is poor and bad, but I prefer something that nothing at all. – serhio Dec 21 '10 at 17:27
  • 1
    This is related: **[How to wait for WaitHandle while serving WPF Dispatcher events?](http://stackoverflow.com/q/21642381/1768303)** – noseratio Feb 09 '14 at 05:23
  • @ColinMackay All the async/await stuff is still the poor man's substitute for writing proper multi-threaded applications. :) – The incredible Jan Apr 19 '23 at 07:18

10 Answers10

146

Try something like this

public static void DoEvents()
{
    Application.Current.Dispatcher.Invoke(DispatcherPriority.Background,
                                          new Action(delegate { }));
}
Fredrik Hedblad
  • 83,499
  • 23
  • 264
  • 266
  • 2
    I even wrote an extension method for application :) `public static void DoEvents(this Application a)` – serhio Dec 21 '10 at 17:46
  • @serhio: Neat extension method :) – Fredrik Hedblad Dec 21 '10 at 17:50
  • 4
    I should remark however that in the real application `Application.Current` sometimes is null... so perhaps its not quite equivalent. – serhio Dec 21 '10 at 18:10
  • Perfect. This should be the answer. Naysayers should not get the credit. – Jeson Martajaya Oct 01 '13 at 19:54
  • Intellisense tells me the first parameter of `Dispatcher.Invoke` is the `delegate`, not the `DispatcherPriority`. I only see 4 overloads for `Invoke`. I'm targeting .NET 4.0. Am I missing something? – Walter Stabosz May 21 '14 at 20:33
  • 7
    This will not always work as it does not push the frame, if an interrupting instruction being made (I.e. a call to a WCF method which in a synchronic continue to this command) chances are you will not see 'refresh' as it will be blocked.. This is why the answer flq provided from the MSDN resource is more correct than this one. – G.Y Sep 26 '14 at 13:00
63

Well, I just hit a case where I start work on a method that runs on the Dispatcher thread, and it needs to block without blocking the UI Thread. Turns out that msdn explains how to implement a DoEvents() based on the Dispatcher itself:

public void DoEvents()
{
    DispatcherFrame frame = new DispatcherFrame();
    Dispatcher.CurrentDispatcher.BeginInvoke(DispatcherPriority.Background,
        new DispatcherOperationCallback(ExitFrame), frame);
    Dispatcher.PushFrame(frame);
}

public object ExitFrame(object f)
{
    ((DispatcherFrame)f).Continue = false;

    return null;
}

(taken from Dispatcher.PushFrame Method)

Some may prefer it in a single method that will enforce the same logic:

public static void DoEvents()
{
    var frame = new DispatcherFrame();
    Dispatcher.CurrentDispatcher.BeginInvoke(DispatcherPriority.Background,
        new DispatcherOperationCallback(
            delegate (object f)
            {
                ((DispatcherFrame)f).Continue = false;
                return null;
            }),frame);
    Dispatcher.PushFrame(frame);
}

See also: https://kent-boogaart.com/blog/dispatcher-frames

Ian Kemp
  • 28,293
  • 19
  • 112
  • 138
flq
  • 22,247
  • 8
  • 55
  • 77
  • Nice find! This looks safer than the suggested implementation by Meleak. I found [a blog post about it](http://kentb.blogspot.de/2008/04/dispatcher-frames.html) – HugoRune Aug 10 '12 at 11:44
  • 2
    @HugoRune That blog post states this approach is unnecessary, and to use the same implementation as Meleak. – Lukazoid Oct 26 '12 at 15:52
  • 1
    @Lukazoid As far as I can tell the simple implementation may cause hard-to-trace lock-ups. (I am not sure about the cause, possibly the problem is code in the dispatcher queue calling DoEvents again, or code in the dispatcher queue generating further dispatcher frames.) In any case, the solution with exitFrame exhibited no such problems so I'd recommend that one. (Or, of course, not using doEvents at all) – HugoRune Oct 26 '12 at 19:03
  • @HugoRune I agree, I believe most situations requiring a DoEvents can be written differently in a way which does not require it. – Lukazoid Oct 27 '12 at 11:36
  • 1
    Showing an overlay on your window instead of a dialog in combination with caliburn's way to involve VMs when the app is closing ruled out callbacks and required us to block without blocking. I would be delighted if you present me a solution without the DoEvents hack. – flq Oct 27 '12 at 12:05
  • Worked OK when I needed to position a data-grid to a certain row on startup. – user1500403 Dec 30 '16 at 07:12
  • 1
    new link to old blog post: https://kent-boogaart.com/blog/dispatcher-frames – CAD bloke Nov 20 '18 at 23:19
  • @HugoRune Who is Meleak? – Mike Nakis Aug 11 '21 at 11:06
26

The old Application.DoEvents() method has been deprecated in WPF in favor of using a Dispatcher or a Background Worker Thread to do the processing as you have described. See the links for a couple of articles on how to use both objects.

If you absolutely must use Application.DoEvents(), then you could simply import the system.windows.forms.dll into your application and call the method. However, this really isn't recommended, since you're losing all the advantages that WPF provides.

BenMorel
  • 34,448
  • 50
  • 182
  • 322
Dillie-O
  • 29,277
  • 14
  • 101
  • 140
  • 1
    I know that is poor and bad, but I prefer something that nothing at all... How can I use Dispatcher in my situation? – serhio Dec 21 '10 at 17:28
  • 3
    I understand your situation. I was in it when I wrote my first WPF app, but I went ahead and took the time to learn the new library and was much better for it in the long run. I highly recommend taking the time. As for your particular case, it looks to me like you'd want the dispatcher to handle the displaying of the coordinates every time your click event fires. You'd need to read up more on the Dispatcher for the exact implementation. – Dillie-O Dec 21 '10 at 17:36
  • no, I'd call `Application.DoEvents` after incrementing myScaleTransform.ScaleX. Don't know if is possible with Dispatcher. – serhio Dec 21 '10 at 17:39
  • @SLaks: didn't try, but this is first that came in mind. Did you try? – serhio Dec 21 '10 at 18:18
  • 15
    Removing Application.DoEvents() is almost as annoying as MS removing the "Start" button on Windows 8. – JeffHeaton Aug 01 '14 at 00:24
  • 3
    No, it was a good thing, that method caused more harm than good. – Jesse Oct 20 '16 at 04:36
  • The windows Start button did lead to a lot of harm it's true, but I'm glad it's back. – Cato Oct 19 '17 at 14:29
  • 2
    I can't believe this is still an issue in this day and age. I remember having to call DoEvents in VB6 to simulate async behaviour. The problem is that a background worker only works when you are not doing UI processing, but UI processing - like for example creating thousands of ListBoxItems can lock up the UI thread. What are we supposed to do when there is really hefty UI work to be done? – Christian Findlay Sep 17 '18 at 02:55
  • 1
    @Christian 4 years later the problem still exists. e.g. StatusBarText = "Updating" it doesn't update the UI. You should not need a background thread to update the UI, as usual Microsoft have overcomplicated something that should be easy. – Paul McCarthy Jul 01 '22 at 10:42
14

If you need just update window graphic, better use like this

public static void DoEvents()
{
    Application.Current.Dispatcher.Invoke(DispatcherPriority.Render,
                                          new Action(delegate { }));
}
6
myCanvas.UpdateLayout();

seems to work as well.

serhio
  • 28,010
  • 62
  • 221
  • 374
3

One problem with both proposed approaches is that they entail idle CPU usage (up to 12% in my experience). This is suboptimal in some cases, for instance when modal UI behavior is implemented using this technique.

The following variation introduces a minimum delay between frames using a timer (note that it is written here with Rx but can be achieved with any regular timer):

 var minFrameDelay = Observable.Interval(TimeSpan.FromMilliseconds(50)).Take(1).Replay();
 minFrameDelay.Connect();
 // synchronously add a low-priority no-op to the Dispatcher's queue
 Application.Current.Dispatcher.Invoke(DispatcherPriority.Background, new Action(() => minFrameDelay.Wait()));
Jonas Chapuis
  • 697
  • 5
  • 9
2

Since the introduction of async and await its now possible to relinquish the UI thread partway through a (formerly)* synchronous block of code using Task.Delay, e.g.

private async void myButton_Click(object sender, RoutedEventArgs e)
{
    Console.WriteLine("scale {0}, location: {1}", 
        myScaleTransform.ScaleX,
        myCanvas.PointToScreen(GetMyByttonLocation()));

    myScaleTransform.ScaleX =
        myScaleTransform.ScaleY =
        myScaleTransform.ScaleX + 1;

    await Task.Delay(1); // In my experiments, 0 doesn't work. Also, I have noticed
                         // that I need to add as much as 100ms to allow the visual tree
                         // to complete its arrange cycle and for properties to get their
                         // final values (as opposed to NaN for widths etc.)

    Console.WriteLine("scale {0}, location: {1}",
        myScaleTransform.ScaleX,
        myCanvas.PointToScreen(GetMyByttonLocation()));
}

I'll be honest, I've not tried it with the exact code above, but I use it in tight loops when I'm placing many items into an ItemsControl which has an expensive item template, sometimes adding a small delay to give the other stuff on the UI more time.

For example:

        var levelOptions = new ObservableCollection<GameLevelChoiceItem>();

        this.ViewModel[LevelOptionsViewModelKey] = levelOptions;

        var syllabus = await this.LevelRepository.GetSyllabusAsync();
        foreach (var level in syllabus.Levels)
        {
            foreach (var subLevel in level.SubLevels)
            {
                var abilities = new List<GamePlayingAbility>(100);

                foreach (var g in subLevel.Games)
                {
                    var gwa = await this.MetricsRepository.GetGamePlayingAbilityAsync(g.Value);
                    abilities.Add(gwa);
                }

                double PlayingScore = AssessmentMetricsProcessor.ComputePlayingLevelAbility(abilities);

                levelOptions.Add(new GameLevelChoiceItem()
                    {
                        LevelAbilityMetric = PlayingScore,
                        AbilityCaption = PlayingScore.ToString(),
                        LevelCaption = subLevel.Name,
                        LevelDescriptor = level.Ordinal + "." + subLevel.Ordinal,
                        LevelLevels = subLevel.Games.Select(g => g.Value),
                    });

                await Task.Delay(100);
            }
        }

On Windows Store, when there's a nice theme transition on the collection, the effect is quite desirable.

Luke

  • see comments. When I was quickly writing my answer, I was thinking about the act of taking a synchronous block of code and then relinquishing the thread back to its caller, the effect of which makes the block of code asynchronous. I don't want to completely rephrase my answer because then readers can't see what Servy and I were bickering about.
Luke Puplett
  • 42,091
  • 47
  • 181
  • 266
  • "its now possible to relinquish the UI thread partway through a synchronously block" No, it's not. You've just *made the code asynchronous*, rather than pumping messages from the UI thread in a synchronous method. Now, a correctly designed WPF application would be one that never blocks the UI thread by synchronously executing long running operations in the UI thread in the first place, using asynchrony to allow the existing message pump to pump messages appropriately. – Servy Nov 03 '14 at 18:36
  • @Servy Under the covers, the `await` will cause the compiler to to sign up the rest of the async method as a continuation on the awaited task. That continuation will occur on the UI thread (same sync context). Control then returns to the caller of the async method, i.e. WPFs eventing subsystem, where events will run until the scheduled continuation runs sometime after the delay period expires. – Luke Puplett Nov 03 '14 at 18:48
  • yes, I'm well aware of that. That's what makes the method asynchronous (the yielding control to the caller and only scheduling a continuation). Your answer states that the method is synchronous when it in fact is using *asynchrony* to update the UI. – Servy Nov 03 '14 at 18:53
  • The first method (the OP's code) is synchronous, Servy. The second example is just a tip for keeping the UI going when in a loop or having to pour items into a long list. – Luke Puplett Nov 03 '14 at 18:55
  • And what you've done is made the synchronous code asynchronous. You haven't kept the UI responsive from within a synchronous method, as your description states, or the answer asks for. – Servy Nov 03 '14 at 18:56
  • The OP wants to solve his problem of the values not reflecting their changes because the UI events haven't run when he takes his second sample readings. He doesn't prescribe keeping the UI responsive within a synchronous method. – Luke Puplett Nov 03 '14 at 19:00
  • True, and had your answer stated that the solution was to make the method asynchronous, instead of using a synchronous method, then it would have been correct. You choose to instead state that the use of `await` here was still using a synchronous method, when it is not. – Servy Nov 03 '14 at 19:03
  • With respect, I think you're being pedantic. My use of synchronous was to the code in its original form, before making it asynchronous. There's arguably an interesting paradox or oxymoron but nothing worth all this fighting and down-voting. – Luke Puplett Nov 03 '14 at 19:11
  • You said that your code allows the UI to be updated from a synchronous method. It doesn't. You made the code *not be synchronous*. If you think having an entirely invalid explanation for your code is a problem then you're going to have to deal with downvotes on your posts. If you actually provide an explanation that is correct, rather than one that is completely incorrect and also misleading, then you wouldn't have that problem. Your description isn't paradoxical or an oxymoron, it's just *wrong*. – Servy Nov 03 '14 at 19:14
0

Make your DoEvent() in WPF:

Thread t = new Thread(() => {
            // do some thing in thread
            
            for (var i = 0; i < 500; i++)
            {
                Thread.Sleep(10); // in thread

                // call owner thread
                this.Dispatcher.Invoke(() => {
                    MediaItem uc = new MediaItem();
                    wpnList.Children.Add(uc);
                });
            }
            

        });
        t.TrySetApartmentState(ApartmentState.STA); //for using Clipboard in Threading
        t.Start();

Work well for me!

0

Push frame:

using System.Windows.Threading;
...
var frame = new DispatcherFrame();
Dispatcher.PushFrame(frame);

Exit frame:

frame.Continue = false;

The following example shows how to use a DispatcherFrame to achieve similar results as the Windows Forms DoEvents method.

https://learn.microsoft.com/dotnet/api/system.windows.threading.dispatcher.pushframe

Updated:

Interesting way to make Task.Delay() non-blocking

// How to use: Task.Delay(250).WaitNB();
public static void WaitNB(this Task task)
{
    var frame = new DispatcherFrame();
    task.ContinueWith(t => frame.Continue = false);
    Dispatcher.PushFrame(frame);
}
Vitaliy
  • 281
  • 1
  • 2
  • 12
-2

Answering the original question: Where is DoEvents?

I think DoEvents is VBA. And VBA does not seem to have a Sleep function. But VBA has a way to get exactly the same effect as a Sleep or Delay. Seems to me that DoEvents is equivalent to Sleep(0).

In VB and C#, you are dealing in .NET. And the original question is a C# question. In C#, you would use Thread.Sleep(0), where 0 is 0 milliseconds.

You need

using System.Threading.Task;

at the top of the file in order to use

Sleep(100);

in your code.

Indinfer
  • 101
  • 8