Не удается обновить пользовательский интерфейс с помощью диспетчера потоков пользовательского интерфейса?

Я разработал приложение WPF на основе шаблона MVVM, и мне нужно получить большой объем данных из веб-службы, и я делаю это с помощью BackgroundWorker внутри ViewModel. Чтобы изменить наблюдаемую коллекцию, я должен использовать диспетчер, и проблема здесь, даже если я передаю ссылку на диспетчер пользовательского интерфейса, он не работает, это похоже на то, что я использую внутренний диспетчер, и мое приложение зависает до тех пор, пока все данные не будут восстановлены.

Я пытаюсь получить Диспетчер пользовательского интерфейса несколькими способами: 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
                 ....
                 }));
}

Похоже, многопоточность не работает


person MaRuf    schedule 18.08.2011    source источник
comment
Если ваше приложение зависает, это выглядит как проблема с BackgroundWorker, а не с Dispatcher. Покажите нам код.   -  person Snowbear    schedule 18.08.2011
comment
BackgroundWorker работает нормально, потому что, если я использую только это, не вызывая веб-сервис, у меня нет никаких проблем, но если я попытаюсь подключиться к веб-сервису внутри события DoWork, я получаю сообщение об ошибке. Вызывающий поток не может получить доступ к этому объекту, потому что им владеет другой поток.   -  person MaRuf    schedule 18.08.2011
comment
@Marco: проблема не в том, что диспетчер сломан, а в том, что вы его используете неправильно. Разместите код.   -  person Dan Puzey    schedule 18.08.2011
comment
У вас ошибка в коде. По вашему вопросу невозможно сказать, где и что это. Вы уже знаете достаточно, чтобы решить свою проблему. Вот совет: создайте новый проект, который делает одну и только одну вещь — обновляет пользовательский интерфейс из BackgroundWorker. Изолируйте свою проблему в как можно меньшем количестве кода. Произойдет одно из двух: во-первых, вы поймете, что делаете неправильно, и исправите свою собственную проблему. Во-вторых, у вас будет та же проблема, но в таком небольшом количестве кода вы можете вставить ее в вопрос и показать всем нам, что вы делаете.   -  person    schedule 18.08.2011
comment
Я разместил код, надеюсь, это поможет   -  person MaRuf    schedule 18.08.2011
comment
@Марко: Это так, это так. Я думаю, ваша проблема в том, что вы непреднамеренно создаете закрытие, которое включает некоторый пользовательский интерфейс или DependencyObject, так что в вашем методе //connect to webservice вы получаете доступ к этому объекту из рабочего потока.   -  person    schedule 18.08.2011
comment
@Rachel Возможно, мне не следует использовать его с этой реализацией, но это не объясняет, почему он не работает, @ Уилл, я не понимаю, что вы имеете в виду, я просто подключаюсь к моему веб-сервису и получаю данные в массив   -  person MaRuf    schedule 18.08.2011


Ответы (2)


Используйте 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);
}
person Rachel    schedule 18.08.2011
comment
Но что, если я хочу сделать более сложные вещи в фоновом потоке, обновляющем коллекцию? - person MaRuf; 18.08.2011
comment
@Марко Как что? В зависимости от того, что вы хотите сделать, некоторые из них можно выполнить в BackgroundWorker, другие — в Dispatcher (только не запускайте вызов диспетчера из фонового потока), а некоторые можно выполнить в потоке пользовательского интерфейса. Все зависит от того, что вы делаете. - person Rachel; 18.08.2011
comment
Так вы говорите мне, что нет способа использовать Dispatcher внутри фонового потока?? это правильно? - person MaRuf; 18.08.2011
comment
@Marco Вы можете использовать Dispatcher внутри BackgroundWorker, но зачем? Оба работают асинхронно с потоком пользовательского интерфейса, поэтому я не вижу причин запускать один внутри другого. Я знаю, что длительные процессы в Dispatcher МОГУТ связать поток пользовательского интерфейса, даже если они запущены в DispatcherPriority.Background. Из-за этого я запускал определенные процессы на BackgroundWorker, в то время как другие процессы запускались на Dispatcher. - person Rachel; 18.08.2011
comment
Хорошо, я до сих пор не понимаю, почему мой код не работает, но я понял вашу точку зрения, спасибо за вашу помощь! - person MaRuf; 18.08.2011
comment
@Marco Марко, мне нужно увидеть, что у вас есть в //Connect to webservice and retrieve data, чтобы понять, почему это не работает, но я предполагаю, что вы получаете доступ к чему-то там, к чему можно получить доступ только из потока пользовательского интерфейса. - person Rachel; 18.08.2011

Вот пример, который может помочь вам реализовать задачу и обновления пользовательского интерфейса. Вы можете легко преобразовать следующий пример в свой сценарий. Вы можете посмотреть мой блог, в котором есть подробное объяснение этой ошибки и решения.

http://bahinenivenkatesh.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 
    }); 
});
person Bathineni    schedule 18.08.2011