当您一次对UI进行大量更新时,我试图找到避免UI锁定的最佳方法。
基本前提是,在启动时,我的工具在后台工作程序中运行perforce FSTAT。这会生成一个非常大的文件列表及其信息。完成此操作后,在其RunWorkerCompleted函数中,我将此信息传播到TreeView内的UI。
然而,这涉及到大量的财产更新!取决于其传播到的文件数。它可以是5000多个文件。这完全锁定了用户界面大约3-5秒。
我想知道我是否可以异步更新用户界面,我说,一次传播10-20个文件&仍然让UI线程继续更新,以便它仍然响应。
谢谢。
答案 0 :(得分:2)
如果要使用属性绑定更新TreeView内部的信息,可以将Binding.IsAsync标志设置为true。如果您没有使用绑定更新值,那么可能需要考虑这些。
另一种选择是更新所有属性,但不要为属性调用PropertyChanged事件(假设您使用INotifyPropertyChanged更新绑定),直到所有数据都被更改,然后调用PropertyChanged对于任务上的每个属性的事件,它仍然是异步,但即使有5000+绑定更新,也不应该花3-5秒。
答案 1 :(得分:1)
你提出的很多建议终于让我得到了一个很好的答案。这是我的代码如下。基本上我们可以使用ReportProgress来允许UI在运行时进行更新。然后调整我们希望这种情况发生的频率。以下是我的解决方案。
关键是每N个项目调用PropegateMetaData(我指定了25个)。然后列表被清空。
这将调用每25个项目的报告进度,然后继续。并最终将其余部分传递给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;
}
}
}
}
}