我有一个视图模型
protected override void Initialize()
{
this.InnerViewModel.LoadInformation();
this.IsInformationLoaded = true;
}
但是,this.InnerViewModel.LoadInformation();
是CPU和IO密集型操作。可能需要几秒钟才能完成。
该模型的视图已绑定到IsInformationLoaded
以显示“正在加载...”屏幕。
我可以想象,我当前的实现并未显示“正在加载...”屏幕,而是在操作完成之前将UI冻结。
如何更改Initialize
方法的更改,以使LoadInformation
异步?请记住,InnerViewModel
也是视图绑定。
修改
以下作品
protected override async void Initialize()
{
await Task.Run(() =>
{
this.InnerViewModel.LoadInformation();
});
this.IsInformationLoaded = true;
}
// In InnerViewModel
public override void LoadInformation()
{
Thread.Sleep(3000);
Application.Current.Dispatcher.Invoke(() =>
{
this.SomethingObservable.Clear();
this.SomethingObservable.Add(...something ...);
});
}
但是如果可能的话,我真的想摆脱Application.Current.Dispatcher
。或者以某种方式将其移入Initialize()
。我不希望InnerModel
知道它是如何被执行的。
答案 0 :(得分:0)
此外,如果InnerViewModel的LoadInformation不知道它是异步执行和/或没有对它进行任何特殊处理,那将是最好的。这可能吗?
在.NET Framework 4.5+中,您可以通过调用BindingOperations.EnableCollectionSynchronization方法启用从后台线程访问数据绑定集合,例如:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DataContext = new WindowViewModel();
}
}
public class WindowViewModel
{
private readonly object _lock = new object();
public ObservableCollection<string> Items { get; } = new ObservableCollection<string>();
public WindowViewModel()
{
BindingOperations.EnableCollectionSynchronization(Items, _lock);
Task.Run(() =>
{
for (int i = 0; i < 100; ++i)
{
Items.Add(i.ToString());
Thread.Sleep(2000);
}
});
}
}
<ListBox ItemsSource="{Binding Items}"></ListBox>
另一个选项是使用调度程序将访问数据绑定集合的操作封送回UI线程:
Task.Run(() =>
{
for (int i = 0; i< 100; ++i)
{
Application.Current.Dispatcher.Invoke(new Action(() => Items.Add(i.ToString())));
Thread.Sleep(2000);
}
});
这种方法使得该方法意识到它确实在后台线程上执行。