模型初始化为异步时,在View中显示“正在加载...”屏幕

时间:2017-01-09 14:48:14

标签: c# wpf xaml

我有一个视图模型

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知道它是如何被执行的。

1 个答案:

答案 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);
                }
            });

这种方法使得该方法意识到它确实在后台线程上执行。