WPF中ViewModel的异步UI更新

时间:2013-06-12 12:43:13

标签: wpf multithreading mvvm-light

我遇到从db获取数据并在UI中异步显示的问题。 我正在使用MVVM灯,当我单击按钮时,在ViewModel中触发了操作:

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

在某些时候,GetFriendsListCompleted由后台线程调用,通知viewmodel该作业已完成。 此时我需要更新ListBox ItemSource。但是当我尝试更新时,我得到了 “调用线程无法访问此对象,因为其他线程拥有它” 我已经尝试了 Dispatcher.CurrentDispatcher.Invoke(),App.Current.Dispatcher.Invoke()和不同的魔法,但它仍然无法正常工作。

我尝试向ViewModel提供UI调度程序,然后从那里调用它 - 没有用。

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

我尝试将此行更改为

Dispatcher.Invoke(DispatcherPriority.Normal,new Action(                         ()=> this.FriendsList.View.Refresh())); - 还是一样。

我正在使用Telerik ListBox来显示项目。 FriendList是CollectionViewSource(http://www.telerik.com/help/wpf/radlistbox-overview.html)。当我在WPF控件示例中使用Telerik示例时,它可以正常工作。当我使用异步方法时,问题开始出现。 视图类型是System.ComponentModel.ICollectionView,它用于过滤和分组。

我还尝试将ObservableCollection分配给ListBox的Items属性,但它也不起作用。

有关_redisModel.GetFriendsListAsync如何工作的更多详细信息: 最后(在所有调用链之后)它最终在这里:

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

在viewmodel中我有方法:

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

有人可以帮帮我吗? 谢谢

2 个答案:

答案 0 :(得分:4)

您正在一个线程中创建CollectionViewSource并在另一个线程(调度程序线程)中刷新它。将您的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();
                     }));
        }
    }
}

答案 1 :(得分:0)

你没有在完成后显示任何实际在后台线程上运行的代码,但我猜测你正在创建一个集合对象,然后你试图将它分配给你的CollectionView。当CV尝试从您的Refresh调用更新(在UI线程上)时,它将尝试使用另一个线程拥有的集合。

如果您包含相关代码,则更容易确定。