WPF - Why does UI Lock Up?

517 views Asked by At

If we use the task based async pattern with async and await keywords, there is no issue with the UI locking up. For example, we can spend 10 seconds loading data from the server and happily display a wait indicator at the same time. However, when running complex tasks on the UI thread, the thread locks up and the animation in the wait indicator just freezes.

Of course, one strategy could be to avoid hefty UI altogether, but this is not really an option in this case. I am loading hundreds of TreeViewItems on to the screen. This is clearly causing the UI to lock up.

I have tried putting the work on the control's Dispatcher like this, but it doesn't help:

   var action = new Action(() =>
    {

       SchemaTree.Items.Clear();

       foreach (var assemblyStructure in assemblyStructures)
       {
           var assemblyNode = CreateTreeNode(SchemaTree.Items, assemblyStructure.Assembly.Name.Replace("Adapt.Model.", string.Empty), assemblyStructure.Assembly, "/Icons/Schema.PNG");

           foreach (var theNameSpace in assemblyStructure.Namespaces)
           {
               var namespaceNode = CreateTreeNode(assemblyNode.Items, theNameSpace.TheNamespace.Name, theNameSpace.TheNamespace, "/Icons/Namespace.PNG");

               foreach (var classInfo in theNameSpace.Classes)
               {
                   CreateClassInfoNode(theNameSpace, classInfo, namespaceNode);
               }
           }
       }
   });

    await SchemaTree.Dispatcher.BeginInvoke(action, DispatcherPriority.Background, null);

If I move the work on to a Task with Task.Run it actually stops the UI from locking up, but obviously, I get a cross threading violation.

  var action = new Action(async () =>
        {
            await Task.Run(() =>
            {
                SchemaTree.Items.Clear();

                foreach (var assemblyStructure in assemblyStructures)
                {
                    var assemblyNode = CreateTreeNode(SchemaTree.Items, assemblyStructure.Assembly.Name.Replace("Adapt.Model.", string.Empty), assemblyStructure.Assembly, "/Icons/Schema.PNG");

                    foreach (var theNameSpace in assemblyStructure.Namespaces)
                    {
                        var namespaceNode = CreateTreeNode(assemblyNode.Items, theNameSpace.TheNamespace.Name, theNameSpace.TheNamespace, "/Icons/Namespace.PNG");

                        foreach (var classInfo in theNameSpace.Classes)
                        {
                            CreateClassInfoNode(theNameSpace, classInfo, namespaceNode);
                        }
                    }
                }
            });

        });


        await SchemaTree.Dispatcher.BeginInvoke(action, DispatcherPriority.Background, null);

Any ideas on how to tell the UI to lessen the priority of this work so that the wait indicator has a chance to animate, and the whole UI doesn't lock up?

1

There are 1 answers

0
Christian Findlay On

The answer to my original question is that the Dispatcher (that handles message pumping) executes everything in a first in first out sequence and is not multi threaded. That means that if you flood the Dispatcher with messages, the UI will lock up. Thankfully, the problem is easy to solve. All I need to do is call

await Dispatcher.Yield(); 

in the loop and the problem magically goes away. What it does is allow other UI work to execute in between the creation of the nodes.

This answer is a placeholder until more detail is added here.