【问题标题】:Show 'Loading...' screen in View while the model is initialized asynchronously在异步初始化模型时在视图中显示“正在加载...”屏幕
【发布时间】:2017-01-09 14:48:14
【问题描述】:

我有一个视图模型

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

【问题讨论】:

  • 如果在方法中设置IsInformationLoaded = false,然后Task.Run(this.InnerViewModel.LoadInformation).ContinueWith(() => {this.IsInformationLoaded = true;});,会发生什么?如果在 Task.Run 之后的下一行将加载的布尔值设置为,它将在任务准备好之前执行该行。
  • 如果这不是问题所在,您能否澄清一下您所说的“Task.Run() 和类似方法 [未成功]”的意思
  • 我已按问题编辑@cubrr
  • 对于您新发现的线程问题,您需要查看this question
  • 这可能会起作用,但是InnerViewModel 会知道它是以异步方式执行的,如果可以的话,我很想避免......

标签: c# wpf xaml


【解决方案1】:

此外,最好 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);
                }
            });

这种方法让方法意识到它确实是在后台线程上执行的。

【讨论】:

  • 但如果我在Task 中执行的方法中执行此操作,BindingOperations.EnableCollectionSynchronization 将不起作用。
  • 按照我的示例在启动后台线程之前在 UI 线程上调用它。不管怎样,要么就是这个,要么就是用dispatcher。
猜你喜欢
  • 2019-11-24
  • 1970-01-01
  • 1970-01-01
  • 2012-10-10
  • 2023-04-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多