2013-06-12 15 views
5

Ich habe ein Problem mit den Daten von db bekommen und asynchron in UI zeigt. ich MVVM Licht verwenden, wenn ich auf die Schaltfläche klicken, wird Aktion in Ansichtsmodell ausgelöst:asynchrones UI-Update von Viewmodel in WPF

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

An einem gewissen Punkt GetFriendsListCompleted von Hintergrund-Thread Notifing genannt wird Viewmodel, dass Arbeit erledigt ist. An dieser Stelle muss ich ListBox ItemSource aktualisieren. Aber wenn ich versuche zu aktualisieren, um die ich bekommen „Der aufrufende Thread dieses Objekt nicht zugreifen, da ein anderer Thread besitzt es“ Ich habe versucht, Dispatcher.CurrentDispatcher.Invoke(), App.Current.Dispatcher.Invoke() und andere Magie, aber es funktioniert immer noch nicht.

Ich versuchte UI Dispatcher Ansichtsmodell zu geben und es dann von dort aus rufen - hat nicht funktioniert.

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. 
     } 
    } 

Ich habe versucht, diese Linie zu

Dispatcher.Invoke (DispatcherPriority.Normal, neue Aktion ( () => this.FriendsList.View.Refresh())) zu ändern; - immer noch das gleiche.

Ich verwende Telerik ListBox Elemente anzuzeigen. FriendList ist CollectionViewSource (http://www.telerik.com/help/wpf/radlistbox-overview.html). Es funktioniert, wenn ich Telerik-Beispiel von WPF-Kontrollbeispielen verwende. Probleme treten auf, wenn ich meine asynchronen Methoden verwende. Art der Ansicht ist System.ComponentModel.ICollectionView, die zum Filtern und Gruppieren verwendet wird.

habe ich auch versucht, nur ObservableCollection Eigenschaft Items der List-Box zuweisen und es funktioniert auch nicht.

ein bisschen mehr Details darüber, wie _redisModel.GetFriendsListAsync funktioniert: Am Ende (nach alle Kette von Anrufen) es endet hier:

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 
    } 
}); 

In Viewmodel Ich habe Methode:

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())); 
      } 
    } 

Kann mir bitte jemand dabei helfen? Danke

+0

@ Robert Kruszewski – LucasSeveryn

+0

Bearbeiten Sie Ihre Frage und fügen Sie Ihre Frage etwas Kontext. Was ist der Typ von 'FriendsList'? Was ist die View-Eigenschaft, auf die Sie von der VM aus zugreifen? – Viv

Antwort

4

Du CollectionViewSource in einem Thread zu schaffen und erfrischend, dass in einem anderen Thread (Dispatcher-Thread). Aktualisieren Sie Ihre GetFriendsListCompleted zu

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

Großartig! Es funktionierte! Vielen Dank. Aber momentan übergebe ich App.Current.Dispatcher in ViewModel innerhalb von CodeBehind. Gibt es einen eleganteren Ansatz? – Andy

+0

@Andy bitte überprüfen akzeptiert Antwort [diese Frage] (http://StackOverflow.com/Questions/2354438/How-to-Pass-the-Ui-Dispatcher-to-the-ViewModel) – sthotakura

+0

Ich habe es ausprobiert und es tut nicht t arbeiten. Es schlägt fehl, nachdem ich dem ViewModel-Konstruktor einen neuen Parameter hinzugefügt habe. MEF konnte einfach nicht finden. Es ist komisch, weil ich bereits einen Parameter habe - meine DAL-Komponente und es funktioniert. Ich denke, es ist etwas damit zu tun, dass WpfContext-Klasse in UI und der Reihenfolge der Initialisierung deklariert ist. – Andy

0

Sie haben keine der Code, der tatsächlich auf dem Hintergrund Thread nach Abschluss ausgeführt wird, aber ich vermute, dass Sie darin erstellen ein Objekt Sammlung, die Sie dann versuchen, zu Ihrem zuweisen Auflistungsansicht Wenn der CV versucht, in Ihrem Refresh-Aufruf (im UI-Thread) zu aktualisieren, versucht er, die Sammlung zu verwenden, die dem anderen Thread gehört.

Wenn Sie den entsprechenden Code enthalten wäre es einfacher, mit Sicherheit zu sagen.

+0

Ihre Schätzung ist ziemlich genau. Ich habe die Fragen mit mehr Code aktualisiert. – Andy

Verwandte Themen