Asheh
Asheh

Reputation: 1597

Avoid UI Lockups when doing lots of PropertyUpdates

I am trying to discover the best way of avoiding UI-Lockups when you are doing a lot of updates to the UI at once.

The basic premise is that on start up my tool runs a perforce FSTAT on the within a background worker. This generates a very large list of files and their information. Once this is completed, in its RunWorkerCompleted function, I then propagate this information to the UI inside of a TreeView.

This however, involves lots of property updates! Depending on the number of files that its propagating to. It can be 5000+ files. This completely locks up the UI for about 3-5 seconds.

I was wondering if I can asynchronously update the UI, such that I say, propagate 10-20 files at once & Still let the UI thread continue to update so that its still responsive.

Thank you.

Upvotes: 3

Views: 117

Answers (2)

Asheh
Asheh

Reputation: 1597

lots of suggestions you made finally got me to a good answer. Here is my code below. Basically we can use ReportProgress to allow the UI to update-while-running. Then adjust for how often we want this to happen. Here is my solution below.

The key is that PropegateMetaData is called for every N number of items (I specified 25). Then the list is emptied.

This will call report progress for every 25 items, then continue on. And eventually pass the rest to WorkerCompleted.

    public static void Refresh(List<string> refreshPaths)
    {
        BackgroundWorker worker = new BackgroundWorker();
        worker.WorkerReportsProgress = true;

        worker.DoWork += delegate(object s, DoWorkEventArgs args)
        {
            List<string> filesPath = null;
            if (refreshPaths == null)
            {
                filesPath = DatabaseViewModel.Instance.Records.Select(record => record.Filepath).ToList();
            }
            else
            {
                filesPath = new List<string>(refreshPaths);
            }

            if (m_Repository != null && filesPath.Count > 0)
            {
                IList<FileSpec> lfs = new List<FileSpec>();

                int index = 0;
                foreach (DataRecord rec in DatabaseViewModel.Instance.Records)
                {
                    lfs.Add(new FileSpec(new LocalPath(rec.Filepath), null));
                    index++;
                    if (index > MaxFilesIteration)
                    {
                        GetFileMetaDataCmdOptions opts = new GetFileMetaDataCmdOptions(GetFileMetadataCmdFlags.AllRevisions, null, null, 0, null, null, null);
                        worker.ReportProgress(0, m_Repository.GetFileMetaData(lfs, null));
                        lfs.Clear();
                        index = 0;
                    }
                }

                args.Result = m_Repository.GetFileMetaData(lfs, null); //pass the remaining results across
            }
        };

        worker.ProgressChanged += (sender, args) => PropegateMetaData(args.UserState as IList<FileMetaData>);
        worker.RunWorkerCompleted += (sender, args) => PropegateMetaData(args.Result as IList<FileMetaData>);
        worker.RunWorkerAsync();
    }

    private static void PropegateMetaData(IList<FileMetaData> fileList)
    {
        IList<FileMetaData> fileState = fileList as IList<FileMetaData>;
        if (fileState != null)
        {
            foreach (FileMetaData fmd in fileState)
            {
                DataRecord currentRecord = DatabaseViewModel.Instance.GetRecordByFilepath(fmd.LocalPath.Path);
                if (currentRecord != null)
                {
                    switch (fmd.Action)
                    {
                        case FileAction.Add:
                            currentRecord.P4Status = P4FileState.Added;
                            break;
                        case FileAction.Edit:
                            currentRecord.P4Status = P4FileState.Edit;
                            break;
                        case FileAction.MoveAdd:
                            currentRecord.P4Status = P4FileState.MoveAdd;
                            break;
                        default:
                            currentRecord.P4Status = P4FileState.None;
                            break;
                    }
                }
            }
        }
    }

Upvotes: 1

Bilal Bashir
Bilal Bashir

Reputation: 1493

If you are updating information inside of the TreeView using property bindings you could set your Binding.IsAsync flag to true. If you aren't updating the values using bindings then that might be something to look into.

Binding.IsAsync Property

Another option would be to update all your properties but, to not call the PropertyChanged event for the property (Assuming you are using INotifyPropertyChanged to update your bindings) until all your data has been changed and then call the PropertyChanged event for each of your properties on a Task so, it is still Async but even with 5000+ binding updates it should not take 3-5 seconds.

Upvotes: 2

Related Questions