I have an existing Wpf application built with Caliburn.Micro and I'd like to start integrating ReactiveUI. To start with, I switched from Caliburn.Micro to the Caliburn.Micro.ReactiveUI library.
I've got a ListBox which is bound using Caliburn Micro's conventions to a BindableCollection. I use CM to locate the view for MyViewModel and the BindableCollection is populated from a search performed with EntityFramework.
I'm trying to implement the asynchronous "compelling example" shown but ReactiveUI.dll is throwing and unhandled InvalidCastException whenever the search is executed. I'm unable to see any details because I don't have ReactiveUI.pdb symbol file. I'm wrapping my BindableCollection property in an ObservableAsPropertyHelper so that as the user executes the search, ReactiveUI will update the BindableCollection so that Caliburn.Micro can do it's thing like usual.
Does anyone know what would be causing the invalid cast exception? I'm somewhat experienced with CM but a total newbie with Rx and RxUI. So, I might be going about this the wrong way entirely.
Here's the relevant code:
public class MainViewModel: ReactiveScreen
{
private ObservableAsPropertyHelper<BindableCollection<MyViewModel>> _myViewModels;
public BindableCollection<MyViewModel> MyViewModels
{
get { return _myViewModels.Value; }
}
[ImportingConstructor]
public MainViewModel(IEventAggregator eventAggregator)
{
_myEntities = new myEntities();
var searchService = new SearchService(_myEntities);
Items.CollectionChanged += delegate
{
NotifyOfPropertyChange(() => Status);
};
var searchTerms = this
.ObservableForProperty(x => x.SearchTerm)
.Value()
.Throttle(TimeSpan.FromSeconds(.5));
var searchResults = searchTerms.SelectMany(searchTerm => searchService.SearchAsync(searchTerm));
var latestMatches = searchTerms
.CombineLatest(searchResults,
(searchTerm, searchResult) =>
searchResult.SearchTerm != searchTerm
? null
: searchResult.Matches)
.Where(matches => matches != null);
_myViewModels= latestMatches.ToProperty(this, x => x.MyViewModels);
}
}
SearchService looks like this:
public class SearchService
{
private MyEntities _myEntities;
public SearchService(MyEntities myEntities)
{
_myEntities= myEntities;
}
public async Task<SearchResult> SearchAsync(string searchTerm)
{
var query = await (from m in _myEntities.Employees select m).ToListAsync();
if (!String.IsNullOrEmpty(searchTerm))
{
searchTerm = searchTerm.Trim();
query = (List<Employee>) query.Where(
x => x.LastName.Contains(searchTerm) || x.FirstName.Contains(searchTerm))
.Select(x => x);
}
return new SearchResult
{
SearchTerm = searchTerm,
Matches = new BindableCollection<MyViewModel>(query.Select(x => new MyViewModel{ Employee = x }))
};
}
}
SearchResult:
public class SearchResult
{
public string SearchTerm { get; set; }
public BindableCollection<MyViewModel> Matches { get; set; }
}
Thanks,
Sean
EDIT:
I got the detail on what the exception was after setting the breakpoint in a different location.
Message:
{"An OnError occurred on an object (usually ObservableAsPropertyHelper) that would break a binding or command. To prevent this, Subscribe to the ThrownExceptions property of your objects"}
Inner Exception:
{"Unable to cast object of type WhereSelectListIterator2[HR.DataContext.Models.Employee,HR.DataContext.Models.Employee]'
to type System.Collections.Generic.List
1[HR.DataContext.Models.Employee]'."}
StackTrace:
at ReactiveUI.RxApp.<>c__DisplayClass5.<.cctor>b__2()
at System.Reactive.Concurrency.Scheduler.Invoke(IScheduler scheduler, Action action)
at System.Reactive.Concurrency.DispatcherScheduler.<>c__DisplayClass1`1.<Schedule>b__0()
at System.Windows.Threading.ExceptionWrapper.InternalRealCall(Delegate callback, Object args, Int32 numArgs)