C# 显示';加载…';异步初始化模型时视图中的屏幕
我有一个视图模型C# 显示';加载…';异步初始化模型时视图中的屏幕,c#,wpf,xaml,C#,Wpf,Xaml,我有一个视图模型 protected override void Initialize() { this.InnerViewModel.LoadInformation(); this.IsInformationLoaded = true; } 但是,this.InnerViewModel.LoadInformation()是非常CPU和IO密集型的操作。可能需要几秒钟才能完成 该模型的视图绑定到IsInformation Loaded,以显示“Loading…”屏幕 您可以想
protected override void Initialize()
{
this.InnerViewModel.LoadInformation();
this.IsInformationLoaded = true;
}
但是,this.InnerViewModel.LoadInformation()代码>是非常CPU和IO密集型的操作。可能需要几秒钟才能完成
该模型的视图绑定到IsInformation Loaded
,以显示“Loading…”屏幕
您可以想象,我当前的实现没有显示“加载…”屏幕,而是在操作完成之前冻结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
知道它是如何执行的
此外,如果InnerViewModel的LoadInformation不知道它正在异步执行和/或不对其进行任何特殊处理,则最好是这样。可能吗
在.NET Framework 4.5+中,可以通过调用方法从后台线程访问数据绑定集合,例如:
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);
}
});
}
}
公共部分类主窗口:窗口
{
公共主窗口()
{
初始化组件();
DataContext=新的WindowViewModel();
}
}
公共类WindowViewModel
{
私有只读对象_lock=新对象();
公共ObservableCollection项{get;}=new ObservableCollection();
公共WindowViewModel()
{
BindingOperations.EnableCollectionSynchronization(项,_锁);
Task.Run(()=>
{
对于(int i=0;i<100;++i)
{
Items.Add(i.ToString());
《睡眠》(2000年);
}
});
}
}
另一个选项是使用dispatcher将访问数据绑定集合的操作封送回UI线程:
Task.Run(() =>
{
for (int i = 0; i< 100; ++i)
{
Application.Current.Dispatcher.Invoke(new Action(() => Items.Add(i.ToString())));
Thread.Sleep(2000);
}
});
Task.Run(()=>
{
对于(int i=0;i<100;++i)
{
Application.Current.Dispatcher.Invoke(新操作(()=>Items.Add(i.ToString()));
《睡眠》(2000年);
}
});
这种方法使方法意识到它确实是在后台线程上执行的。如果在方法中设置IsInformationLoaded=false
,然后Task.Run(This.InnerViewModel.LoadInformation)。ContinueWith(()=>{This.IsInformationLoaded=true;})会发生什么情况代码>?如果您将加载的布尔值设置为Task.Run之后的下一行,它将在任务准备就绪之前执行该行。如果这不是问题所在,您是否可以澄清您所说的“Task.Run()
和类似方法[未成功]”的意思。我已通过question@cubrr编辑了您新发现的线程问题,您需要查看。这可能会起作用,但InnerViewModel
会知道它是以异步方式执行的,如果可以的话,我很想避免这种情况……但是,BindingOperations.EnableCollectionsSynchronization
如果在任务中执行的方法中执行,则无法工作。在启动后台线程之前,在UI线程上调用它,如我的示例所示。不管怎样,要么是这个,要么是使用调度程序。
Task.Run(() =>
{
for (int i = 0; i< 100; ++i)
{
Application.Current.Dispatcher.Invoke(new Action(() => Items.Add(i.ToString())));
Thread.Sleep(2000);
}
});