2011-05-10 45 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,特別是如果只有一個或幾個事件需要傳遞給視圖模型。