我好像是围成一圈。
我有一个WPF应用程序,它有一个带状态栏的主功能区窗口。导航到“视图”时,用户控件将显示为主窗口的内容。
视图有一个ViewModel,它处理从数据库中检索数据,View的datacontext设置为ViewModel。
我想要的是在后台线程上运行冗长的操作(数据检索),同时在主窗口中运行状态以进行适当的报告。后台任务完成后,状态应恢复为“就绪”(与Visual Studio非常相似)。
我应该如何将它连接在一起,以便我可以在ViewModel中分离出数据访问代码,同时保持响应式用户界面?
我尝试过使用BackgroundWorker是代码中的各个地方,但我最终仍然没有响应的UI。
答案 0 :(得分:1)
您可以将文本块(进度条)绑定到依赖项属性。然后在单独的线程中执行您的操作并根据进度更新依赖项属性。不要忘记你必须在正确的线程中执行此操作,因此请存储调度程序。
答案 1 :(得分:1)
我发现BackgroundWorker的界面不便于此,所以我更喜欢直接使用ThreadPool。
这是基本想法:
public class MyViewModel
{
public SomeCollectionType<Widget> Widgets
{
get
{
if(!WidgetFillQueued)
{
WidgetFillQueued = true;
ThreadPool.QueueUserWorkItem(_ =>
{
WidgetLoadStatus = 0;
int totalCount = FetchWidgetCount();
while(_internalWidgetCollection.Count < totalCount && !AbortFill)
{
_internalWidgetCollection.AddRange(FetchMoreWidgets());
WidgetLoadStatus = (double)_internalWidgetCollection.Count / totalCount;
}
WidgetLoadStatus = null; // To indicate complete
});
}
return _internalWidgetCollection;
}
}
}
假设WidgetLoadStatus是一个从UI绑定的DependencyProperty,WPF的绑定系统会处理保持状态栏进度显示更新所需的线程转换。
假设_internalWidgetCollection允许多线程访问并正确实现INotifyPropertyChanged,所有集合更新也将导致UI线程上的UI更新。
在我的实际视图模型中有许多集合,因此我使用的数据结构跟踪所有当前正在执行的操作并计算显示的组合状态值,而不是使用WidgetFillQueued和WidgetLoadStatus等单独的属性。但是上面的代码给出了如何正确实现线程的基本思路。
以上也适用于加载单个大对象,例如文件下载:不是每次调用AddRange(),只是累积数据直到全部下载,然后设置包含数据的属性。请注意,如果对象本身包含DispatcherObjects,则必须在UI线程上对其进行反序列化。通过从线程中调用Dispatcher.BeginInvoke来执行此操作。