我正在尝试使用我的silverlight应用程序调用wcf服务,并且我在理解模型如何将结果返回到视图模型时遇到一些麻烦。在我的视图模型中,我有以下命令:
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 = results)但是我似乎无法让此集合更新/通知视图模型结果。我只使用一个方法和虚拟类生成并运行类似的测试,它似乎工作,所以我怀疑这个问题是由于异步调用/线程。我在ViewModel中有INotifyPropertyChanged来同步View和ViewModel。我是否还需要在模型中实现INotifyPropChng?我是mvvm的新手,所以我将不胜感激任何帮助/示例。
谢谢,
更新 在进一步测试中,我将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。
谢谢,
答案 0 :(得分:2)
我发现最好将我的WCF服务封装在另一层Service类中。这使我可以更轻松地单元测试我的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);
}
您的ViewModel将如下所示:
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 :(得分: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; }
}
当其SearchResults集合填充了适当的数据时,模型会触发SearchResultsRetrieved事件。我更喜欢自定义事件,而不是在我的模型上实现INotifyPropertyChanged,特别是如果只有一个或几个事件需要传递给视图模型。