2013-06-12 10 views
5

Mam problem z pobieraniem danych z db i pokazywaniem w interfejsie asynchronicznym. Używam MVVM światło, gdy klikam przycisk, akcja jest wyzwalane w ViewModel:asynchroniczna aktualizacja UI z ViewModel w WPF

private void SearchQuery(string query) 
    { 
     _redisModel.GetFriendsListAsync(query); 
    } 

W pewnym momencie GetFriendsListCompleted nazywany jest przez wątek tła notifing ViewModel, że praca jest wykonywana. W tym momencie muszę zaktualizować ItemSource ListBox. Ale gdy próbuję aktualizować to mam „Nić wywołujący nie ma dostępu do tego obiektu, ponieważ inny wątek jest właścicielem” Próbowałem Dispatcher.CurrentDispatcher.Invoke(), App.Current.Dispatcher.Invoke() i inna magia, ale nadal nie działa.

Próbowałem przekazać dyspozytora UI do ViewModel, a następnie wywołać go stamtąd - nie działa.

private string filterText = string.Empty; 
    public string FilterText 
    { 
     get { return filterText; } 
     set 
     { 
      filterText = value; 
      this.RaisePropertyChanged(() => this.FilterText); 

      this.FriendsList.View.Refresh(); // Here where exception is happening. 
     } 
    } 

Próbowałem zmienić tę linię do

Dispatcher.Invoke (DispatcherPriority.Normal, new Action ( () => this.FriendsList.View.Refresh())); - wciąż ten sam.

Używam Telerik ListBox do wyświetlania elementów. FriendList to CollectionViewSource (http://www.telerik.com/help/wpf/radlistbox-overview.html). Działa, gdy używam przykładu Telerik z przykładów kontroli WPF. Problemy pojawiają się, gdy używam swoich metod asynchronicznych. Typ widoku to System.ComponentModel.ICollectionView służy do filtrowania i grupowania.

Próbowałem również przypisać ObservableCollection do właściwości Items ListBox i to też nie działa.

trochę więcej szczegółów na temat _redisModel.GetFriendsListAsync działa: W końcu (po całej sieci połączeń) kończy się tutaj:

public GetAsyncResult(Func<T> workToBeDone, Action<IAsyncResult> cbMethod, Object state) 
{ 
    _cbMethod = cbMethod; 
    _state = state; 
    QueueWorkOnThreadPool(workToBeDone); 
} 

ThreadPool.QueueUserWorkItem(state => 
{ 
    try 
    { 
    _result = workToBeDone(); 
    } 
    catch (Exception ex) 
    { 
     _exception = ex; 
    } 
    finally 
    { 
    UpdateStatusToComplete(); //1 and 2 
    NotifyCallbackWhenAvailable(); //3 callback invocation 
    } 
}); 

W ViewModel mam metoda:

private void GetFriendsListCompleted(object sender, ResultsArgs<Friend> e) 
    { 
     if (!e.HasError) 
     { 
      var curr = e.Results; 
      if (curr != null) 
      { 
       this.FriendsList= new CollectionViewSource(); 

       this.FriendsList.Source = list; 
       this.FriendsList.Filter += this.FriendFilter; 
       FilterText = ""; 

       Dispatcher.Invoke(DispatcherPriority.Normal, new Action(

         () => this.FriendsList.View.Refresh())); 
      } 
    } 

Czy ktokolwiek może mi w tym pomóc? Dziękujemy

+0

@Robert Kruszewski – LucasSeveryn

+0

Edytuj swoje pytanie i dodać jakiś kontekst do Twojego pytania. Jaki jest typ 'FriendsList'. Jaka jest właściwość 'Widok', z której uzyskujesz dostęp za pomocą maszyny wirtualnej? – Viv

Odpowiedz

4

Tworzysz CollectionViewSource w jednym wątku i odświeżasz to w innym wątku (wątku dispatchera). Aby zaktualizować swój GetFriendsListCompleted

private void GetFriendsListCompleted(object sender, ResultsArgs<Friend> e) 
{ 
    if (!e.HasError) 
    { 
     var curr = e.Results; 
     if (curr != null) 
     { 
      Dispatcher.Invoke(DispatcherPriority.Normal, new Action(
        () => { 
        this.FriendsList= new CollectionViewSource(); 
        this.FriendsList.Source = list; 
        this.FriendsList.Filter += this.FriendFilter; 
        FilterText = ""; 
        this.FriendsList.View.Refresh(); 
        })); 
     } 
    } 
} 
+0

Świetnie! Zadziałało! Dziękuję Ci bardzo. Ale obecnie przechodzę App.Current.Dyspozytor w ViewModel wewnątrz CodeBehind. Czy istnieje bardziej eleganckie podejście? – Andy

+0

@Andy sprawdź zaakceptowaną odpowiedź [to pytanie] (http://stackoverflow.com/questions/2354438/how-to-pass-the-ui-dispatcher-to-the-viewmodel) – sthotakura

+0

Wypróbowałem i nie działa " t działa. Nie powiedzie się po dodaniu nowego parametru do konstruktora ViewModel. MEF po prostu nie mógł znaleźć. To dziwne, ponieważ mam już jeden parametr - mój składnik DAL i działa. Myślę, że ma to coś wspólnego z deklaracją klasy WpfContext w interfejsie użytkownika i kolejnością inicjowania. – Andy

0

Nie pokazałeś żadnego kodu, który faktycznie działa w wątku w tle po ukończeniu, ale domyślam się, że w nim tworzysz obiekt kolekcji, który następnie próbujesz przypisać do swojego CollectionView. Gdy CV próbuje zaktualizować (w wątku UI) z wywołania Refresh, spróbuje użyć kolekcji, której właścicielem jest drugi wątek.

Jeśli podasz odpowiedni kod, łatwiej będzie powiedzieć na pewno.

+0

Twoje przypuszczenie jest dość dokładne. Mam zaktualizować pytania z więcej kodu. – Andy

Powiązane problemy