mikem4a1
mikem4a1

Reputation: 21

WPF Multithreading UI Lockup

I am trying to create a load window which will have a progress bar and display this while the background worker does some time intensive work. Unfortunately, this load window only partially loads then freezes until the background work is completed. How can I fix this so that the load window does not conflict with the background processes?

A visual example: https://i.sstatic.net/JHXvv.png


        lw = new LoadWindow();
        lw.Show();            

        worker.DoWork += delegate(object s, DoWorkEventArgs args)
        {
            /*Program-specific code*/

            string theDirectory = System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location); /* get the folder location of the exe */
            string filePath = theDirectory + "\\export.xml";
            if (!File.Exists(filePath))
            {
                FileStream f = File.Create(filePath);
                f.Close();
            }

            FileStream fileStream = File.Open(filePath, FileMode.Open);
            fileStream.SetLength(0);
            fileStream.Close();

            FileStream fsWrite = File.Open(filePath, FileMode.Open, FileAccess.Write, FileShare.None);
            StreamWriter sw = new StreamWriter(fsWrite);

            Dispatcher.Invoke(new Action(delegate() { 

                /*Program-specific code*/                    

                System.Diagnostics.ProcessStartInfo startInfo = new System.Diagnostics.ProcessStartInfo("cmd.exe", "/C svn log " + FolderPath.Text + " -v --xml");
                startInfo.CreateNoWindow = true;
                startInfo.RedirectStandardOutput = true;
                startInfo.UseShellExecute = false;
                using (System.Diagnostics.Process process = Process.Start(startInfo))
                {

                    using (StreamReader reader = process.StandardOutput)
                    {
                        string outStr = reader.ReadToEnd();
                        sw.Write(outStr);
                        sw.Close();
                        fsWrite.Close();
                        reader.Close();
                        process.WaitForExit();
                    }
                }
                TicketIDChanged(sender, new TextChangedEventArgs(e.RoutedEvent, new UndoAction()));
            }), DispatcherPriority.Background);             
        };

        worker.RunWorkerCompleted += delegate(object s, RunWorkerCompletedEventArgs args)
        {
            lw.Hide();
        };

        worker.RunWorkerAsync();

Upvotes: 1

Views: 166

Answers (4)

Ganesh Gontina
Ganesh Gontina

Reputation: 1

With the .NET 4.0 release, we got the Task Parallel Library (TPL) which provided APIs for performing Parallel and Task Based Asynchronous Operations. The TPL provides classes like Task and Parallel which were used for performing asynchronous programming and parallel operations respectively. In .NET 4.5 with C# 5.0 we got the async and await keywords for representing asynchronous methods. In WPF, we can make use of the Task class for creating responsive and interactive applications.

Upvotes: 0

Henk Holterman
Henk Holterman

Reputation: 273701

You are using Dispatcher.Invoke() to run a big chunk of code (from StartProcess to WaitForExit) on the GUI thread. Yes that will block.

And I don't see the reason for the Invoke. Running a process is well suited for running in DoWork().

The only part that needs syncing is the TicketIDChanged() at the end. Do that with Dispatcher.Invoke() or move it to the Completed event.

Upvotes: 1

dwonisch
dwonisch

Reputation: 5795

By using Dispatcher.Invoke() you are delegating all the work within the delegate onto the UI-Thread. If this is a time intensive operation it'll get stuck here.

Try to use Dispatcher.Invoke() for updating UI states only. If this isn't easily accessible, try to refactor your code and add multiple invocations.

You can also set worker.WorkerReportsProgress = true and use the worker.ReportProgress(int percent, object args) method to update a simple progress bar on your loading screen. The dispatching will then be handled by the BackgroundWorker.

Try to invoke only the TickedIDChanged-method, for example.

Upvotes: 1

Shumii
Shumii

Reputation: 4581

Looks like you are doing the work on the UI thread not background. I might be wrong because im not 100% sure what the background priority means. However, what i would advise you try is to not do the async work inside a dispatcher invoke... And if part of the async work is to assigment to something on the UI thread then do just the assignment in a dispatcher invoke inside the completed handler along with the hide like you do.

Upvotes: 0

Related Questions