How to run and interact with an async Task from a WPF gui

前端 未结 4 1241
隐瞒了意图╮
隐瞒了意图╮ 2020-11-28 01:33

I have a WPF GUI, where I want to press a button to start a long task without freezing the window for the duration of the task. While the task is running I would like to get

4条回答
  •  孤独总比滥情好
    2020-11-28 01:37

    This is a simplified version of the most popular answer here by Bijan. I simplified Bijan's answer to help me think through the problem using the nice formatting provided by Stack Overflow.

    By carefully reading and editing Bijan's post I finally understood: How to wait for async method to complete?

    In my case the chosen answer for that other post is what ultimately led me to solve my problem:

    "Avoid async void. Have your methods return Task instead of void. Then you can await them."

    My simplified version of Bijan's (excellent) answer follows:

    1) This starts a task using async and await:

    private async void Button_Click_3(object sender, RoutedEventArgs e)
    {
        // if ExecuteLongProcedureAsync has a return value
        var returnValue = await Task.Run(()=>
            ExecuteLongProcedureAsync(this, intParam1, intParam2, intParam3));
    }
    

    2) This is the method to execute asynchronously:

    bool stillWorking = true;
    internal void ExecuteLongProcedureAsync(MainWindow gui, int param1, int param2, int param3)
    {
        //Start doing work
        gui.UpdateWindow("Work Started");
    
        while (stillWorking)
        {
            //put a dot in the window showing the progress
            gui.UpdateWindow(".");
    
            //the following line blocks main thread unless
            //ExecuteLongProcedureAsync is called with await keyword
            System.Threading.Thread.Sleep(50);
        }
    
        gui.UpdateWindow("Done and Done");
    } 
    

    3) Invoke the operation which involves a property from gui:

    void UpdateWindow(string text)
    {
        //safe call
        Dispatcher.Invoke(() =>
        {
            txt.Text += text;
        });
    }
    

    Or,

    void UpdateWindow(string text)
    {
        //simply
        txt.Text += text;
    }
    

    Closing comments) In most cases you have two methods.

    • First method (Button_Click_3) calls the second method and has the async modifier which tells the compiler to enable threading for that method.

      • Thread.Sleep in an async method blocks the main thread. but awaiting a task does not.
      • Execution stops on current thread (second thread) on await statements until task is finished.
      • You can't use await outside an async method
    • Second method (ExecuteLongProcedureAsync) is wrapped within a task and returns a generic Task object which can be instructed to be processed asynchronously by adding await before it.

      • Everything in this method in executed asynchronously

    Important:

    Liero brought up an important issue. When you are Binding an element to a ViewModel property, the property changed callback is executed in UI thread. So there is no need to use Dispatcher.Invoke. Value changes fired by INotifyPropertyChanged are automatically marshalled back onto the dispatcher.

提交回复
热议问题