WPF MVVM Многопоточность - PullRequest
       6

WPF MVVM Многопоточность

0 голосов
/ 08 октября 2010

Я видел другие проблемы, похожие на мои, но я не видел ни одной, которую можно было бы применить, чтобы заставить мой код работать.

Так что я новичок в MVVM, и я пытаюсь получить некоторые вещи, которые выполняются в фоновом потоке, для обновления моего пользовательского интерфейса. Что я замечаю, так это то, что в первый раз запускается пользовательский интерфейс, а фоновый поток выполняется в первый раз, если коллекция IEnumerable <> пользовательский интерфейс не полностью обновлен по отношению к резервным данным. Если коллекция ObservableCollection <>, выдается ошибка.

Из того, что я прочитал, изменения в коллекциях должны выполняться в потоке диспетчера, но вызовы OnPropertyChanged () этого не делают. Итак, кто-то, пожалуйста, скажите мне, как это могло произойти:

Я изменяю свою наблюдаемую коллекцию _Printers:

foreach (PrinterViewModel pv in _Printers)
            {
                DispatcherExec(() =>
                {
                var abilities = from x in _ServerData.Types
                                select new PrinterAbility(
                                    new PrintableType() { ID = x.ID, Name = x.Name, NumInProcUnit = x.NumInProcUnit, PrintersMappedTo = x.PrintersMappedTo, SysName = x.SysName },
                                    x.PrintersMappedTo.Contains(pv.Printer.ID)
                                    );


                    pv.Printer.SetAbilities(abilities);
                });

Мой DispatcherExec выглядит так:

 private void DispatcherExec(Action action)
    {
        //Dispatcher.Invoke((Action)delegate 
        //{
        //    action.BeginInvoke(null, null); 
        //}, null);
        Dispatcher.CurrentDispatcher.Invoke((Action)delegate
        {
            action.Invoke();
        }, null);
    }

А вот код SetAbilities, который не работает:

 public void SetAbilities(IEnumerable<PrinterAbility> abilities)
    {
        if (log.IsInfoEnabled)
            log.Info("SetAbilities(IEnumerable<PrinterAbility> abilities): called on printer "+Name);

        List<PrinterAbility> l = new List<PrinterAbility>();
        abilities.ForEach(i =>
            {
                i.PrinterAbilityChanged += new PrinterAbilityChangedEventHandler(OnPrinterAbilityChanged);
                l.Add(i);
            }
            );
        lock (_Abilities)
        {
            foreach (PrinterAbility pa in l)
                _Abilities.Add(pa);
        }
        if (log.IsDebugEnabled)
            log.Debug("SetAbilities(IEnumerable<PrinterAbility> abilities): leaving");
    }

В добавлении к наблюдаемой коллекции _Abilities.Add (pa) говорится: «Этот тип CollectionView не поддерживает изменения в его SourceCollection из потока, отличного от потока Dispatcher». Я думаю: «Вы шутите?»

Кроме того, я думаю, что изменение объекта в наблюдаемой коллекции автоматически вызовет OnCollectionChanged (), верно?

Заранее всем спасибо.

Ответы [ 3 ]

2 голосов
/ 08 октября 2010

Использование Dispatcher.CurrentDispatcher - это не то, что вы должны делать из потока BG.Вам нужно использовать Dispatcher для объекта, производного от DependencyObject, который был создан в потоке пользовательского интерфейса.

Кроме того, вы перебираете объекты * ViewModel (PrinterViewModel) из потока BG.Это действительно идет вразрез с MVVM.Ваша модель должна выполнять асинхронные операции, а ваши ViewModel должны обрабатывать эти асинхронные операции таким образом, чтобы представление могло потреблять (путем маршалинга в нужный поток с помощью Dispatcher).

Кроме того, вы 'закрытие по переменной цикла (pv).Плохо, плохоЭто (в зависимости от порядка выполнения) может означать, что к тому времени, когда появится диспетчер, вы получите несколько вызовов pv.Printer.SetAbilities (...) для одного и того же экземпляра PrinterViewModel.Создайте локальную переменную внутри цикла и используйте ее в своем анонимном методе, чтобы избежать этой проблемы.

0 голосов
/ 08 октября 2010

Вы должны использовать Dispatcher, связанный с любым из ваших элементов управления WPF, а не Dispatcher.CurrentDispatcher для фонового потока.

Также

Dispatcher.CurrentDispatcher.Invoke((Action)delegate
        {
            action.Invoke();
        }, null);

является избыточным, оно должно быть

wpfDispatcher.Invoke(action, null);

И, наконец, для первого блока обычно следует избегать передачи переменных цикла в лямбда-выражениятрюк временного назначения, чтобы обойти эти хитрые проблемы закрытия.Почти уверен, что это не проблема в этом случае.

0 голосов
/ 08 октября 2010

Может быть , это и , это поможет изменить коллекцию Observable через потоки.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...