【问题标题】:Cannot update UI using the UI thread Dispatcher?无法使用 UI 线程 Dispatcher 更新 UI?
【发布时间】:2011-08-18 12:25:36
【问题描述】:

我设计了一个基于 MVVM 模式的 WPF 应用程序,我需要从 Web 服务获取大量数据,我使用 ViewModel 中的 BackgroundWorker 来完成。要修改可观察的集合,我必须使用调度程序,问题就在这里,即使我通过引用 UI 调度程序它也不起作用,就像我使用内部调度程序并且我的应用程序被冻结,直到所有数据都被检索。

我尝试通过多种方式获取 UI Dispatcher,Dispatcher.CurrentDispatcher、Application.Dispatcher、App.Current.Dispatcher...做了一些研究,我读到它应该可以工作,有人有什么建议吗?

谢谢 马可

更新

这里有一些代码:这就是我将 Dispatcher 传递给 ModelView 的方式

void AppWindow_Loaded(object sender, RoutedEventArgs e)
{
     this.DataContext = new ModelViewApplication(System.Windows.Threading.Dispatcher.CurrentDispatcher);
}

那我尝试用这种方式获取数据

public ModelViewApplication(Dispatcher _dispatcher)
{
    BackgroundWorker bw = new BackgroundWorker();
    bw.DoWork += new DoWorkEventHandler(getData);
    bw.RunWorkerCompleted += new RunWorkerCompletedEventHandler(completedData);

    bw.RunWorkerAsync();
}

public void getData(object sender, DoWorkEventArgs e)
{
    _dispatcher.Invoke(DispatcherPriority.Normal,
        new Action(
            delegate()
                 {
                 //Connect to webservice and retrieve data
                 ....
                 }));
}

多线程好像不行

【问题讨论】:

  • 如果您的应用程序被冻结,那么它看起来像是BackgroundWorker 的问题,而不是Dispatcher。向我们展示代码。
  • BackgroundWorker 工作正常,因为如果我只使用不调用 webservice 我没有任何问题,但是如果我尝试在 DoWork 事件中连接到 webservice 我收到错误“调用线程无法访问这个对象,因为不同的线程拥有它”
  • @Marco:问题不在于调度程序坏了——而是您对它的使用可能不正确。贴一些代码。
  • 您的代码中有错误。无法从您的问题中看出它在哪里或是什么。您已经知道足以解决您的问题。这里有个提示:创建一个新项目,它只做一件事——从 BackgroundWorker 更新 UI。用尽可能少的代码隔离您的问题。两种情况中的任何一种都会发生:第一,你会弄清楚你做错了什么,你会解决你自己的问题。二,你会遇到同样的问题,但是在这么少的代码中你可以将它粘贴到一个问题中,让我们都看看你在做什么。
  • 我贴了一些代码,希望对你有帮助

标签: wpf mvvm backgroundworker dispatcher


【解决方案1】:

使用BackgroundWorker 获取您的数据,然后使用RunWorkerCompleted 更新ObservableCollection。你根本不需要Dispatcher

public ModelViewApplication()
{
    BackgroundWorker bw = new BackgroundWorker();
    bw.DoWork += new DoWorkEventHandler(getData);
    bw.RunWorkerCompleted += completedData;

    bw.RunWorkerAsync();
}

public void getData(object sender, DoWorkEventArgs e)
{
    //Connect to webservice and retrieve data
    e.Result = WebService.GetData();
}

private void completedData(object sender, RunWorkerCompletedEventArgs e)
{
    MyCollection = new ObservableCollection<SomeClass>((IList)e.Results);
}

【讨论】:

  • 但是如果我想在后台线程中做一些更复杂的事情来更新集合呢?
  • @Marco 喜欢什么?取决于你想要做什么,其中一些可以在BackgroundWorker 中完成,其他可以通过Dispatcher 完成(只是不要从后台线程运行调度程序调用),还有一些可以在用户界面线程。这一切都取决于你在做什么。
  • 那么你是在告诉我没有办法在后台线程中使用 Dispatcher 吗?对吗?
  • @Marco 您可以在 BackgroundWorker 中使用 Dispatcher,但您为什么要这样做?两者都与 UI 线程异步运行,所以我真的看不出有理由在另一个线程中运行一个。我确实知道 Dispatcher 上长时间运行的进程会占用 UI 线程,即使它们在 DispatcherPriority.Background 上运行也是如此。因此,我会在BackgroundWorker 上运行某些进程,而其他进程将在Dispatcher 上运行
  • 好吧,我还是不明白为什么我的代码不起作用,但我明白了你的意思,谢谢你的帮助!!
【解决方案2】:

这是一个示例,可以帮助您实现任务和 UI 更新。您可以轻松地将以下示例转换为您的场景。 你可以看看我的博客,里面有关于这个错误和解决方案的详细解释

http://bathinenivenkatesh.blogspot.com/2011/07/wpf-build-more-responsive-ui.html

ObservableCollection images = new ObservableCollection(); 
TaskFactory tFactory = new TaskFactory(); 
tFactory.StartNew(() => 
{ 
    for (int i = 0; i < 50; i++) 
    { 
        //GET IMAGE Path FROM SERVER 
        System.Windows.Application.Current.Dispatcher.BeginInvoke((Action)delegate() 
        { 
            // UPDATE PROGRESS BAR IN UI 
        }); 

        images.Add((""); 
    } 

}).ContinueWith(t => 
{ 
    if (t.IsFaulted) 
    { 
        // EXCEPTION IF THREAD IS FAULT 
        throw t.Exception; 
    } 
    System.Windows.Application.Current.Dispatcher.BeginInvoke((Action)delegate() 
    { 
        //PROCESS IMAGES AND DISPLAY 
    }); 
});

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-04-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-05
    相关资源
    最近更新 更多