2011-09-19 11 views
17

Konwertuję parser czatu dla gry, w którą gram, napisałem w C# winformach do wpf, głównie po to, aby uzyskać lepszą obsługę MVVM i wpf. Oto spływać jak mam swój projekt utworzeniaCzy korzystanie z Dispatchera w moim ViewModelu jest złe?

Zobacz: Dla Teraz tylko proste ListBox z ItemSource związanego z moich ViewModels collection obserwowalne czat

Modelu: mam stwardnienie znaki, które mogą być zalogowane za jednym razem, a każda postać ma klasę czatu. Klasa czatu uruchamia pracownika działającego w tle, który pobiera i rozpoczyna kolejną linię czatu z gry i odpala zdarzenie o nazwie IncomingChat w tym wierszu.

public event Action<Game.ChatLine> IncomingChat; 

Używam tle Pracownik na ogień zdarzenie w moich backgroundworkers progresschaged imprezę, bo kiedy był przy użyciu timera Ciągle dostaję problem wątków. Najpierw poprawiłem to, zmieniając mój Timer na DispatchTimer, ale nie wydawało mi się, żebym miał DispatchTimer w moim modelu.

ViewModel: Ponieważ mam wiele znaków, tworzę wiele ChatViewModels. Przekazuję znak do konstruktora ChatViewModels i subskrybuję wydarzenie czatu. Tworzę ObservableColleciton do przechowywania linii czatu po odebraniu tego wydarzenia. Teraz otrzymuję problem z wątkiem na moim viewModel, gdy próbuję dodać linię, którą otrzymuję od mojego wydarzenia na czacie do mojego observablecollection.

mam wokół to poprzez moje ViewModels obsługi zdarzenia przychodzące rozmowy wyglądać tak

public ObservableCollection<Game.ChatLine) Chat {get; private set;} 

void Chat_Incoming(Game.ChatLine line) 
{ 
    App.Current.Dispatcher.Invoke(new Action(delegate 
    { 
    Chat.Add(line) 
    }), null); 
} 

nie czuć prawo do mnie chociaż. Chociaż to działa, używanie Dispatchera w moim modelu widoku wydaje mi się nie na miejscu.

Odpowiedz

37

Mimo że działa, używanie Dispatchera w moim modelu widokowym w tym stylu wydaje mi się nie na miejscu.

To nie jest podejście całkowicie nieuzasadnione i jest podejściem, które podejmuje wiele osób. Osobiście, jeśli używasz WPF (lub Silverlight 5) i masz dostęp do licencji TPL, wolę korzystać z licencji TPL, aby sobie z tym poradzić.

Zakładając, że ViewModel jest zbudowany na wątku UI (to znaczy: przez widok, lub w odpowiedzi na widoku zdarzeń związanych), co ma miejsce w przypadku prawie zawsze IMO, można dodać to do konstruktora:

// Add to class: 
TaskFactory uiFactory; 

public MyViewModel() 
{ 
    // Construct a TaskFactory that uses the UI thread's context 
    uiFactory = new TaskFactory(TaskScheduler.FromCurrentSynchronizationContext()); 
} 

Następnie, gdy pojawi się wydarzenie, można to wykorzystać, aby je zebrać:

void Chat_Incoming(Game.ChatLine line) 
{ 
    uiFactory.StartNew(() => Chat.Add(line)); 
} 

pamiętać, że jest nieznacznie inny niż oryginalny, ponieważ to już nie blokuje (jest to bardziej jak za pomocą BeginInvoke zamiast Invoke). Jeśli trzeba to zablokować aż UI zakończeniu przetwarzania wiadomości, można użyć:

void Chat_Incoming(Game.ChatLine line) 
{ 
    uiFactory.StartNew(() => Chat.Add(line)).Wait(); 
} 
+1

Używany jest kontekst synchronizacji. Ten wzorzec jest używany w całym tekście, więc Twój kod do gwintowania może być zapisany w sposób ogólny i nadal będzie dostępny bez względu na to, czy robisz wygrane, wcf, wpf, wf lub ASP.NET (domyślam się, że ostatni). – Will

+1

@Will: OP używał WPF (w tekście), ale tak, to zadziała dla każdego technika, dlatego tak bardzo to lubię ... –

+1

+1, nie wiedziałem, że TPL ma harmonogram który mógłby korzystać z Kontakty synchronizacji. Mój kod nadal bezpośrednio korzystał z wysyłania/wysyłania w SynchronizationContext. –

0

Widok modelu jest dobrym miejscem, aby zrobić synchronizację wątku. Usuń DispatcherTimer z modelu i pozwól, aby VM go obsłużyła.

+0

W powyższym przykładzie nie ma DispatcherTimer - zdarzenie przychodzące nie jest oczywiście zdarzeniem DispatcherTimer, ponieważ dzieje się na wątku tła. –

+0

Tak, ale jak czytałem z ciała pytania, poco użył DispatcherTimer w modelu. Nie powiedział wyraźnie, że usunął go później, z tego, co widzę. –

0

Uwielbiam odpowiedź Reeda i zgadzam się z twoją obawą, że coś jest nie tak z twoim użyciem Dispatcher. Twoje referencje maszyny wirtualnej: App, co jest moim zdaniem odniesieniem do artefaktu interfejsu użytkownika (lub kontrolki). Zamiast tego użyj lub jeszcze lepiej wstrzyknij prawidłową instancję Dispatcher do maszyny wirtualnej, co pozwoli uniknąć konieczności tworzenia instancji maszyny wirtualnej w wątku interfejsu użytkownika.

Powiązane problemy