使用ReactiveUI和Caliburn.Micro

时间:2016-01-18 20:26:55

标签: wpf reactiveui caliburn.micro.reactiveui

我尝试使用Caliburn.Micro.ReactiveUI实现列表框的分页机制,使用" .Skip(currentPage).Take(pageSize)"调用EF。我是ReactiveUI和Reactive的新手。我确信这应该很容易。

我有一个" SearchParameters"我需要观察的类,当SearchParameters对象的任何属性发生变化时,搜索函数需要执行。

您可以从已注释掉的代码中看到我尝试将该类定义为ReactiveObject。目前的实现是使用CM的PropertyChangedBase。使用CM的约定,单个属性是我视图中的绑定文本框:

public class SearchParameters : PropertyChangedBase
    {

        private string _searchTerm;
        public string SearchTerm
        {
            get { return _searchTerm; }
            set
            {
                if (value == _searchTerm) return;
                _searchTerm = value;
                NotifyOfPropertyChange(() => SearchTerm);

            }
        }
        private int _pageSize;
        public int PageSize
        {
            get { return _pageSize; }
            set
            {
                if (value == _pageSize) return;
                _pageSize = value;
                NotifyOfPropertyChange(() => PageSize);

            }
        }

        private int _skipCount;
        public int SkipCount
        {
            get { return _skipCount; }
            set
            {
                if (value == _skipCount) return;
                _skipCount = value;
                NotifyOfPropertyChange(() => SkipCount);

            }
        }

        //private string _searchTerm;
        //public string SearchTerm
        //{
        //    get { return _searchTerm; }
        //    set { this.RaiseAndSetIfChanged(ref _searchTerm, value); }
        //}
        //private int _pageSize;
        //public int PageSize
        //{
        //    get { return _pageSize; }
        //    set { this.RaiseAndSetIfChanged(ref _pageSize, value); }
        //}

        //private int _skipCount;
        //public int SkipCount
        //{
        //    get { return _skipCount; }
        //    set { this.RaiseAndSetIfChanged(ref _skipCount, value); }
        //}

    }

" SearchService"当SearchParameter的任何一个值发生变化时,需要执行以下方法:

public async Task<SearchResult> SearchAsync(SearchParameters searchParameters)
    {
        return await Task.Run(() =>
        {
            var query = (from m in _hrEntities.Departments select m);
            if (!String.IsNullOrEmpty(searchParameters.SearchTerm))
            {
                searchParameters.SearchTerm = searchParameters.SearchTerm.Trim();
                query = query.Where(
                    x => x.Employee.LastName.Contains(searchParameters.SearchTerm) || x.Employee.FirstName.Contains(searchParameters.SearchTerm)).Skip(searchParameters.SkipCount).Take(searchParameters.PageSize);
            }
            return new SearchResult
            {
                SearchTerm = searchParameters.SearchTerm,
                Matches = new BindableCollection<DepartmentViewModel>(query.Select(x => new DepartmentViewModel{ Department = x }).Skip(searchParameters.SkipCount).Take(searchParameters.PageSize))
            };
        });
    }

以下是我试图在MainViewModel的ctor中连接所有这些内容以及Rx对我来说朦胧的地方:

public class MainViewModel : ReactiveScreen
{
private SearchParameters _searchParameters;
        public SearchParameters SearchParameters
        {
            get { return _searchParameters; }
            set
            {
                if (value == _searchParameters) return;
                _searchParameters = value;
                NotifyOfPropertyChange(() => SearchParameters);
            }
        }
{

public void MainViewModel()
{
    var searchService = new SearchService();
    //default Skip and PageSize values
    SearchParameters = new Services.SearchParameters { SkipCount = 0 , PageSize = 10};

    var searchParameters = this.ObservableForProperty(x => x.SearchParameters)
                .Value()
                .Throttle(TimeSpan.FromSeconds(.3));
    var searchResults = searchParameters.SelectMany(parameters => searchService.SearchAsync(parameters));
            var latestMatches = searchParameters
               .CombineLatest(searchResults,    
                   (searchParameter, searchResult) =>
                       searchResult.SearchTerm != searchParameter.SearchTerm
                           ? null
                           : searchResult.Matches)
               .Where(matches => matches != null);
    _departmentViewModels = latestMatches.ToProperty(this, x => x.DepartmentViewModels);
            searchParameters.Subscribe(x => Debug.WriteLine(x));
}
}

在上面的示例中,对SearchAsync的调用不会执行。似乎没有观察到对SearchParameter属性的更改。

谁能告诉我我在这里做错了什么?

1 个答案:

答案 0 :(得分:0)

以下是我最终如何做到这一点,尽管如果有人有建议,我有兴趣听取其他解决方案。我不确定这是否是最佳方式,但它有效:

首先,我在SearchParameters类中定义了一个计算属性,该属性返回一个字符串,并在View中更新CurrentPage,SkipCount和PageSize时重新评估:

public string ParameterString
        {
            get { return String.Format("SearchTerm={0}|SkipCount={1}|PageSize={2}", SearchTerm, SkipCount, PageSize); }
        }

接下来,在我的MainViewModel ctor中,我只是观察计算而不是单独尝试对SearchTerm,SkipCount和PageSize做出反应(我原来的问题是询问该怎么做):

var searchTerms = this
                .ObservableForProperty(x => x.SearchParameters.ParameterString)
                .Value()
                .Throttle(TimeSpan.FromSeconds(.3));

var searchResults = searchTerms.SelectMany(parameters => SearchService.SearchAsync(parameters));
            var latestMatches = searchTerms
               .CombineLatest(searchResults,
                   (searchTerm, searchResult) =>
                       searchResult.SearchTerm != searchTerm
                           ? null
                           : searchResult.Matches)
               .Where(matches => matches != null);

最后,在我的SearchService中,我解析参数字符串以获取当前值:

var parameters = searchParameters.Split('|');
            var searchTerm = "";
            var skipCount = 0;
            var pageSize = 0;
            foreach (var parameter in parameters)
            {
                if (parameter.Contains("SearchTerm="))
                {searchTerm = parameter.Replace("SearchTerm=", "");}
                else if (parameter.Contains("SkipCount="))
                { skipCount = Convert.ToInt32(parameter.Replace("SkipCount=", "")); }
                else if (parameter.Contains("PageSize="))
                { pageSize = Convert.ToInt32(parameter.Replace("PageSize=", "")); }
            }