【问题标题】:WPF Multithreading: Using Dispatcher but the UI still hangs?WPF 多线程:使用 Dispatcher 但 UI 仍然挂起?
【发布时间】:2023-03-05 20:58:01
【问题描述】:

即使我使用的是 Dispatcher,我的 UI 挂起也有一点问题,在进一步了解之前,我想知道这是否是我处理数据检索的方式。

现在我的主窗口创建了一个 View 和 ViewModel。然后,在一个新线程内部(使用 Dispatcher),它设置 View.DataContext = ViewModel。当绑定启动时会延迟创建一个非常大的 ObservableCollection,从而导致速度减慢。但是,似乎应该在该减速之前显示的其他一些 UI 项目实际上并未显示。

   private void ButtonClick(Object sender, RoutedEventArgs e)
   {
        MyView view = new MyView();
        MyViewModel vm = new MyViewModel();

        TabItem tabItem = new TabItem();
        tabItem.Header = "MyView";
        tabItem.Content = view;

        MyTabCollection.Items.Add(tabItem);

        Window working = new Working();
        working.Show();

        ThreadStart thread = delegate()
        {
            DispatcherOperation operation = Dispatcher.BeginInvoke(
                DispatcherPriority.Normal,
                new Action(delegate()
                {
                    view.DataContext = vm;
                    ((FrameworkElement)view.Parent).Focus();
                    working.Close();
                }
                )
            );
        };

        Thread theThread = new Thread(thread);
        theThread.Start();
    }

这基本上是说它应该创建一个视图和一个视图模型,然后将视图添加到我拥有的选项卡集合中(这意味着它至少应该显示新选项卡)。而且,它还应该显示一个“工作...”窗口。之后,应该有一个单独的线程将 ViewModel 链接到视图,关注该选项卡并关闭工作窗口。问题是第一部分直到一切都完成后才会显示;直到新线程实际完成后(这导致工作窗口立即显示/关闭),该选项卡才会显示,工作窗口也不会显示。我猜这可能与我检索数据的方式有关,但我不确定。这是它的做法:

  1. 创建视图
  2. 创建视图模型
  3. 创建 TabItem 并将 Content 设置为 View,并将 TabItem 添加到 TabCollection。
  4. 创建/显示“工作...”窗口
  5. 调度程序:设置 View.DataContext = ViewModel。此事件触发 DataBindings,后者又获取 ObservableCollection。由于 OC 是延迟创建的,因此现在正在创建它(这是瓶颈)。
  6. 调度程序:将焦点设置到选项卡
  7. 关闭“工作中...”窗口

【问题讨论】:

    标签: wpf multithreading data-binding mvvm dispatcher


    【解决方案1】:

    您的所有额外线程正在做的是将另一个调用编组回调度程序线程。大概你真的想在额外的线程上做工作,或者创建它没有意义。

    理想情况下,您的额外线程应该适当地获取所有数据,而您只需在调度程序线程中实际连接所有数据。重要的是决定哪些工作需要在 UI 线程上完成,哪些工作需要在后台线程上完成。

    【讨论】:

    • 我有点明白你在说什么...但是如果我将我的类分开如下:View、ViewModel、Model 和 DAL (DataAccessLayer) 我在哪里投入这个单独的线程所以它真的有效吗?
    • @myermian:这真的取决于你的代码是如何工作的。您可以通过异步调用调用您的 DAL 或模型以 get 数据,然后在数据全部到位时编组回 UI 线程。
    【解决方案2】:

    显然你对问题的分析是正确的。您的视图模型在需要时延迟加载数据,直到 Dispatcher 回调才会发生这种情况,此时您再次回到 UI 线程,一切都被锁定。

    在我看来,解决方案是在数据访问层做线程:

    对于集合:您可以定义仅返回已从上游数据源加载的项目的特殊集合,然后在有人订阅 INotifyCollectionChanged 时触发在单独的线程上加载其他项目。当附加项目到达时,触发 INotifyCollectionChanged 事件。当 INotifyCollectionChanged 被取消订阅时,取消任何挂起的加载。

    对于总计等:相同的想法。随着数据的进入,总的增加和事件的发生(对于 DependencyProperty 或使用 INotifyPropertyChanged 自动)。

    此外,数据层应该对每个集合、求和或其他延迟加载的值有一个并行属性,指示它是否已完全加载,从而允许 UI 将未完全加载的部分显示为灰色。在某处有一个整体“正在加载”标志也很方便,当任何东西正在加载时,它可以用来使 UI 部分变灰(这种方式更容易编写 UI)。

    请注意,有时操作必须阻塞,直到检索到实际数据。我认为在这种情况下最简单的事情是在数据层提供方法来强制同步加载数据。

    【讨论】:

    • +1,我使用了“work-in-process”收集模式,效果很好。您可以在 VS 2010 的 Add References 对话框中看到这种方法的好处(在 VS 2008 中,加载 GAC 中的可用引用列表需要很长时间)以及 Windows 7 的卸载程序窗口。使用此技术要记住的一件事是,必须将实际的 INotifyCollectionChanged 回调分派到 UI 线程,并且在修改集合时必须小心。我发现拥有一个纯粹用于将项目“传输”到 UI 线程的“缓冲区”集合很有帮助。
    【解决方案3】:

    您的DispatcherPriority 设置为Normal - 尝试将其设置为Background,因为这可能会改善渲染效果

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2012-06-11
      • 2014-01-04
      • 2019-07-08
      • 1970-01-01
      • 2011-10-07
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多