Make wpf UI responsive when button click is perfor

2019-04-09 02:48发布

问题:

In my wpf (c#) application , a long process is performed when a button is pressed by the user. When button is pressed until the complete code executed the window freezes and user cannot perform any other task in the window. How can I make the button click code as a background process, so that the window become responsive to the user. I have tried the following method, but was unsuccessful.

private void btn_convert_Click(object sender, RoutedEventArgs e)
{
    Dispatcher.CurrentDispatcher.BeginInvoke(DispatcherPriority.Background, 
    new Action(() => { 

         WorkerMethod();
    }));
}

Where WorkerMethod is the function with the entire code. Any suggessions.

Regards,
Sangeetha

回答1:

What you have actually done here is asking the UI thread to run your heavy task through the Dispatcher.

You should create a new thread/Task and let it run your background Method:

Thread t = new Thread(() => WorkerMethod());
t.Start();

for further information read this:

WPF BackgroundWorker vs. Dispatcher

Understanding “Dispatcher” in WPF



回答2:

If you are targeting .NET 4.5, you can use async/await to make your UI responsive:

private async void btn_convert_Click(object sender, RoutedEventArgs e)
{
    await WorkerMethod();
}

Modify your WorkerMethod:

private Task WorkerMethod()
{
    return Task.Run(() =>
        {
           // your long running task here
        });
}


回答3:

An alternative to the answers here is a BackgroundWorker. It has the added benefit of returning you to the GUI thread after it's done.

private void btn_convert_Click(object sender, RoutedEventArgs e)
{
    BackgroundWorker worker = new BackgroundWorker();
    worker.DoWork += WorkerMethod;
    worker.RunWorkerCompleted += WorkerEnded;
    worker.RunWorkerAsync();
}

private void WorkerMethod()
{
    // New thread, cannot access GUI
}

private void WorkerEnded()
{
    // Back on the GUI Thread
    // Worker is finished
}


回答4:

You are calling the method on the UI-Dispatcher, this will make the UI freeze. Create a new thread in your click-event using

private void btn_convert_Click(object sender, RoutedEventArgs e)
    {
         Thread t = new Thread(new ThreadStart(WorkerMethod));
         //assign STA or MTA, etc....
         t.Start();
    }

Look here for information on "System.Threading.Thread`.



回答5:

I have imagined another way I am quite proud of: In case the multithread have benefit only to allow IHM to animate a progress bar, if the user is not supposed to interact with the ihm suring the process or simply if there is no time to developp multithreading and you cannot allow your application to freeze without a feedback:

you could develop a loader launched into another STA thread that created another window and displays this loader in the middle of the frozen window.

Here is a post from which I have made an article and an implementation of such a solution:

http://charly-studio.com/blog/a-loader-for-wpf-long-ui-process/

Here is how I use my component:

private void MainWindow_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
    // Long UI process context
    using (new LongUIProcessContext("Wait while loading..."))
    {
        Thread.Sleep(2000);

        // Sub long ui process context
        using (new LongUIProcessContext("Wait while loading 2..."))
        {
            Thread.Sleep(2000);
        } 

        // Another sub long ui process context
        using (new LongUIProcessContext("Wait while loading 3..."))
        {
            Thread.Sleep(2000);
        }
    }
}

And here is what the bench I propose looks like:

And here is the direct link of the bench I made in which you can find the components:

The bench solution direct download



回答6:

Try to use the Backgroundworker class. You have to register to the DoWork event and create an event handler where you put all your processing code in. You start the worker with the RunWorkerAsync method. There is also a method that can report your workers progress "ReportProgress". The RunWorkerCompleted will show you when your worker is done processing your code. Try to search on the internet after BackgroundWorker and you will find plenty of examples how to use it