2009-02-26 7 views
35

W aplikacji WPF, którą piszę przy użyciu wzorca MVVM, mam proces w tle, który robi to, ale trzeba pobrać aktualizacje statusu z niego do interfejsu użytkownika.Sprawdzanie, czy funkcja OnPropertyChanged() jest wywoływana w wątku UI w aplikacji WPF MVVM

Używam wzorca MVVM, więc mój ViewModel nie wie praktycznie nic o widoku (UI), który przedstawia model użytkownikowi.

Say Mam następujące metody w moim ViewModel:

public void backgroundWorker_ReportProgress(object sender, ReportProgressArgs e) 
{ 
    this.Messages.Add(e.Message); 
    OnPropertyChanged("Messages"); 
} 

Moim zdaniem Mam ListBox powiązany z właściwością Messages (a) z List<string> ViewModel. OnPropertyChanged spełnia rolę interfejsu INotifyPropertyChanged, dzwoniąc pod numer PropertyChangedEventHandler.

Muszę upewnić się, że OnPropertyChanged jest wywoływana w wątku interfejsu użytkownika - jak to zrobić? Próbowałem następujące:

public Dispatcher Dispatcher { get; set; } 
public MyViewModel() 
{ 
    this.Dispatcher = Dispatcher.CurrentDispatcher; 
} 

następnie dodając następujące metody OnPropertyChanged:

if (this.Dispatcher != Dispatcher.CurrentDispatcher) 
{ 
    this.Dispatcher.Invoke(DispatcherPriority.Normal, new ThreadStart(delegate 
    { 
     OnPropertyChanged(propertyName); 
    })); 
    return; 
} 

ale to nie działa. Jakieś pomysły?

Odpowiedz

33

WPF automatycznie wprowadza poprawki właściwości do wątku interfejsu użytkownika. Jednak nie powoduje on zmiany kolekcji, więc podejrzewam, że dodanie wiadomości powoduje awarię.

Możesz samodzielnie dodać użytkownika ręcznie (patrz przykład poniżej) lub użyć czegoś takiego, jak this technique Napisałem przez jakiś czas na blogu.

Ręczne przetaczanie:

public void backgroundWorker_ReportProgress(object sender, ReportProgressArgs e) 
{ 
    Dispatcher.Invoke(new Action<string>(AddMessage), e.Message); 
    OnPropertyChanged("Messages"); 
} 

private void AddMessage(string message) 
{ 
    Dispatcher.VerifyAccess(); 
    Messages.Add(message); 
} 
+0

to zrobił, z jednym niewielkim modyfikacji - zmieniłem Lista do ObservableCollction i działa jak czar. Dzięki! –

+1

Jeśli możliwe jest obserwowanie "Wiadomości", nie ma potrzeby wywoływania dla niego funkcji OnPropertyChanged(). – Doug

3

Miałem podobny scenariusz Tylko w tym tygodniu (MVVM tutaj zbyt). Miałem oddzielną klasę robiącą jej rzecz, raportującą status powrotu na treserze zdarzeń. Program obsługi zdarzeń był wywoływany zgodnie z oczekiwaniami i mogłem zobaczyć, że wyniki wracają na czas z Debug.WriteLine's.

Ale z WPF, bez względu na to, co zrobiłem, UI nie zaktualizuje się, dopóki proces nie zostanie zakończony. Natychmiast po zakończeniu procesu interfejs użytkownika zaktualizuje się zgodnie z oczekiwaniami. Wyglądało to tak, jakby uzyskiwał on właściwość PropertyChanged, ale czekał na zakończenie wątku przed aktualizacjami interfejsu użytkownika naraz.

(Ku mojemu przerażeniu, ten sam kod w Windows.Forms z DoEvents i .Refresh() działa jak czar.)

tej pory, mam rozwiązać ten przez rozpoczęciem procesu na własną rękę Temat:

//hook up event handler 
myProcess.MyEvent += new EventHandler<MyEventArgs>(MyEventHandler); 

//start it on a thread ... 
ThreadStart threadStart = new ThreadStart(myProcess.Start); 

Thread thread = new Thread(threadStart); 

thread.Start(); 

a następnie w obsługi zdarzeń:

private void MyEventHandler(object sender, MyEventArgs e) { 
.... 
Application.Current.Dispatcher.Invoke(
       DispatcherPriority.Send, 
       (DispatcherOperationCallback)(arg => 
       { 
     //do UI updating here ... 
     }), null); 

nie będę polecał ten kod, ponieważ jestem wciąż stara się zrozumieć model gwintu WPF, jak działa dyspozytorskie, WH W moim przypadku interfejs użytkownika nie był aktualizowany, dopóki proces nie został zakończony, nawet jeśli program obsługi zdarzenia został wywołany zgodnie z oczekiwaniami (według projektu?). Ale do tej pory zadziałało to dla mnie.

Znalazłem te dwa linki pomocne:

http://www.nbdtech.com/blog/archive/2007/08/01/Passing-Wpf-Objects-Between-Threads-With-Source-Code.aspx

http://srtsolutions.com/blogs/mikewoelmer/archive/2009/04/17/dealing-with-unhandled-exceptions-in-wpf.aspx

0

obsłużyć zdarzenie BackgroundWorker.ReportProgress poza moim modelu widoku i przekazać faktyczną instancję BackgroundWorker i ViewModel do mojej klasy, która definiuje metodę (-y) asynch.

Metoda asynch wywołuje wtedy bgWorker.ReportProgress i przekazuje klasę, która opakowuje delegata jako nazwę użytkownika (jako obiekt). Delegat piszę jako anonimową metodę.

W procedurze obsługi zdarzenia rzutuję go z obiektu z powrotem do typu opakowania, a następnie wywołuję delegata w środku.

Wszystko to oznacza, że ​​mogę kodować zmiany w interfejsie użytkownika bezpośrednio z kodu, który działa asynchronicznie, ale po prostu ma to owijanie wokół niego.

To wyjaśnia bardziej szczegółowo:

http://lukepuplett.blogspot.com/2009/05/updating-ui-from-asynchronous-ops.html

8

naprawdę lubię Jeremy'ego odpowiedź: Dispatching In Silverlight

Podsumowanie:

  • Umieszczenie Dispatcher w ViewModel wydaje nieeleganckiego

  • Tworzenie Action < Akcja> Nieruchomość, ustawić go tylko uruchomić działania w konstruktorze VM

  • Przy użyciu VM z V, ustawić właściwość działania do powoływania dyspozytor
0

To więcej przedłużenia do zaakceptowanej odpowiedzi, ale zrobiłem to z moim obsługi zdarzeń ...

using System.Threading; 

private void Handler(object sender, RoutedEventArgs e) 
{ 
    if (Thread.CurrentThread == this.Dispatcher.Thread) 
    { 
     //do stuff to this 
    } 
    else 
    { 
     this.Dispatcher.Invoke(
      new Action<object, RoutedEventArgs>(Handler), 
      sender, 
      e); 
    } 
} 
Powiązane problemy