WPF多线程:使用Dispatcher但UI仍然挂起?

时间:2010-11-10 16:28:58

标签: wpf multithreading data-binding mvvm dispatcher

即使我正在使用Dispatcher,我的UI仍然存在一些问题,在我进一步研究之前,我想知道这是否是我处理数据检索的方式。

现在我的主窗口创建了一个View和ViewModel。然后,在新的Thread内部(使用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. 创建ViewModel
  3. 创建将Content设置为View的TabItem,并将TabItem添加到TabCollection。
  4. 创建/显示“工作...”窗口
  5. Dispatcher:设置View.DataContext = ViewModel。此事件会引发DataBindings,而DataBindings又会获取ObservableCollection。由于OC是Lazily创建的,现在正在创建(这是瓶颈)。 < - 这会弄乱我的单独线程/调度员吗?
  6. Dispatcher:将Focus设置为选项卡
  7. 关闭“工作...”窗口

3 个答案:

答案 0 :(得分:5)

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

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

答案 1 :(得分:2)

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

在我看来,解决方案是在数据访问层中进行线程化:

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

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

此外,数据层应具有与每个集合,sum或其他延迟加载值的并行属性,以指示它是否已完全加载,从而允许UI使未完全加载的部分变灰。在某处可以使用整个“加载”标记也很方便,可以在任何内容加载时用于灰显UI部分(更容易以这种方式编写UI)。

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

答案 2 :(得分:0)

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