2011-05-10 54 views
2

我想用我的silverlight应用程序调用wcf服务,并且在理解模型如何将结果返回给视图模型时遇到了一些麻烦。在我的视图模型我有以下命令:在Silverlight中使用mvvm进行异步调用

public DelegateCommand GetSearchResultCommand 
    { 
     get 
     { 
      if (this._getSearchResultCommand == null) 
       this._getSearchResultCommand = new DelegateCommand(GetSearchResultCommandExecute, CanGetSearchResultsCommandExecute); 

      return this._getSearchResultCommand; 
     } 

    } 

private void GetSearchResultCommandExecute(object parameter) 
    { 

     this.SearchResults = this._DataModel.GetSearchResults(this.SearchTerm); 
    } 
/// <summary> 
    /// Bindable property for SearchResults 
    /// </summary> 
    public ObservableCollection<QueryResponse> SearchResults 
    { 
     get 
     { 
      return this._SearchResults; 
     } 
     private set 
     { 
      if (this._SearchResults == value) 
       return; 

      // Set the new value and notify 
      this._SearchResults = value; 
      this.NotifyPropertyChanged("SearchResults"); 
     } 
    } 

然后我的模型中,我有以下代码

public ObservableCollection<QueryResponse> GetSearchResults(string searchQuery) 
    { 
     //return type cannot be void needs to be a collection 
     SearchClient sc = new SearchClient(); 
     //****** 
     //TODO: stubbed in placeholder for Endpoint Address used to retreive proxy address at runtime 
     // sc.Endpoint.Address = (clientProxy); 
     //****** 

     sc.QueryCompleted += new EventHandler<QueryCompletedEventArgs>(sc_QueryCompleted); 
     sc.QueryAsync(new Query { QueryText = searchQuery }); 
     return LastSearchResults; 
    } 

    void sc_QueryCompleted(object sender, QueryCompletedEventArgs e) 
    { 
     ObservableCollection<QueryResponse> results = new ObservableCollection<QueryResponse>(); 
     results.Add(e.Result); 
     this.LastSearchResults = results; 
    } 

当我插入模式,我看到正在执行的查询和内断点结果在模型中返回(this.LastSearchResults =结果),但我似乎无法得到此集合来更新/通知结果的视图模型。我已经生成并运行了一个类似的测试,只使用了一个方法和虚拟类,它似乎工作,所以我怀疑这个问题是由于异步调用/线程。我在ViewModel中有INotifyPropertyChanged来同步View和ViewModel。我是否也需要在模型中实现INotifyPropChng?我是mvvm的新手,所以对于我应该如何处理这个问题的任何帮助/例子都会很感激。

谢谢

UPDATE 在进一步的测试中,我加入INotifyPropertyChanged的模型并改变Completed事件如下:

void sc_QueryCompleted(object sender, QueryCompletedEventArgs e) 
    { 
     ObservableCollection<QueryResponse> results = new ObservableCollection<QueryResponse>(); 
     results.Add(e.Result); 
     //this.LastSearchResults = results; 
     SearchResults = results; 

    } 

在搜索结果我现在看到它是放置手表更新了WCF的结果。我的问题仍然是这个正确的方法吗?它似乎现在工作,但我很好奇,如果我失去了其他东西,或者如果我不应该在模型中放置INotify。

谢谢

回答

2

我发现,这是最好的概括了我的WCF服务,服务类的附加层。这让我更容易单元测试我的ViewModels。这样做有几种模式,尽管这是我用过的最简单的模式。该模式是创建一个匹配服务调用定义的方法,但也包含可在服务调用完成后调用的Action。

public class Service : IService 
{ 
    public void GetSearchResults(string searchQuery, Action<ObservableCollection<QueryResponse>> reply) 
    { 
     //return type cannot be void needs to be a collection 
     SearchClient sc = new SearchClient(); 
     //****** 
     //TODO: stubbed in placeholder for Endpoint Address used to retreive proxy address at runtime 
     // sc.Endpoint.Address = (clientProxy); 
     //****** 

     sc.QueryCompleted += (s,e) => 
     { 
      ObservableCollection<QueryResponse> results = new ObservableCollection<QueryResponse>(); 
      results.Add(e.Result); 
      reply(results); 
     }; 

     sc.QueryAsync(new Query { QueryText = searchQuery }); 
    } 
} 

您还可以提供ViewModel可以使用的接口。这使单元测试更容易,虽然是可选的。

public interface IService 
{ 
    void GetSearchResults(string searchQuery, Action<ObservableCollection<QueryResponse>> reply); 
} 

您的视图模型然后会是这个样子:

public class MyViewModel : INotifyPropertyChanged 
{ 
    private IService _service; 

    public MyViewModel() 
     : this(new Service()) 
    { } 

    public MyViewModel(IService service) 
    { 
     _service = service; 

     SearchResults = new ObservableCollection<QueryResponse>(); 
    } 

    private ObservableCollection<QueryResponse> _searchResults 
    public ObservableCollection<QueryResponse> SearchResults 
    { 
     get { return _searchResults; } 
     set 
     { 
     _searchResults = value; 
     NotifyPropertyChanged("SearchResults"); 
     } 
    } 

    public void Search() 
    { 
     _service.GetSearchResults("abcd", results => 
     { 
     SearchResults.AddRange(results); 
     }); 
    } 

    protected void NotifyPropertyChanged(string property) 
    { 
     var handler = this.PropertyChanged; 
     if(handler != null) 
     handler(new PropertyChangedEventArgs(property)); 
    } 
} 

用于封装您服务的另一个原因调用到这样的另一个类是,它可以提供这样的事情在一个地方的记录和错误处理。这样,您的ViewModel本身就不需要关心与服务特别相关的事情。

+1

感谢乔,在看着这个我认为那么命令(我如何调用搜索)保持不变,与我的委托指令?主要的变化是在模型中引用创建的服务接口? – rlcrews 2011-05-10 15:10:13

+0

这是对的,而不是我创建的公共Search()方法,您仍然可以使用您的搜索命令。 – 2011-05-10 15:53:54

+1

这是一个很好的回应。即使你没有为Silverlight做Async,将通信抽象为服务层也是我的首选方法。这也使得多个ViewModel更容易使用相同的服务,并使服务层模拟更加简单。 – 2011-05-11 14:32:44

0

我可能会用线沿线的东西:

public class ViewModel : INotifyPropertyChanged 
{ 
    private readonly IModel model; 
    private readonly DelegateCommand getSearchResultsCommand; 

    public DelegateCommand GetSearchResultsCommand 
    { 
     get { return getSearchResultsCommand; } 
    } 
    public ObservableCollection<QueryResponse> SearchResults 
    { 
     get { return model.SearchResults; } 
    } 

    public ViewModel(IModel model) 
    { 
     this.model = model; 
     this.model.SearchResultsRetrieved += new EventHandler(model_SearchResultsRetrieved); 

     this.getSearchResultsCommand = new DelegateCommand(model.GetSearchResultCommandExecute, model.CanGetSearchResultsCommandExecute); 
    } 

    private void model_SearchResultsRetrieved(object sender, EventArgs e) 
    { 
     this.NotifyPropertyChanged("SearchResults"); 
    } 

} 

public interface IModel 
{ 
    event EventHandler SearchResultsRetrieved; 

    void GetSearchResultCommandExecute(object parameter); 
    bool CanGetSearchResultsCommandExecute(object parameter); 

    ObservableCollection<QueryResponse> SearchResults { get; } 
} 

随着SearchResultsRetrieved事件由模型被解雇时,其SearchResult所收集已经充满了相应的数据。我更喜欢自定义事件,而不是在我的模型上实现INotifyPropertyChanged,特别是如果只有一个或几个事件需要传递给视图模型。