问题
In our app we use ReactiveUI to follow the MVVM pattern. In one view we want to show a UITableView. Data are usually passed to a UITableView.Source but with ReactiveUI we use ReactiveTableViewSource<TSource>
.
What I don't understand is how I can bind my data that I got in my view model in a ReactiveList
to the ReactiveTableViewSource
.
What we now have is that we create a table inside a UIView like this:
Table = new UITableView(UIScreen.MainScreen.Bounds);
Table.Source = new TableSource(Table, MyReactiveList, (NSString) CellIdentifier, _cellHeight, cell => Debug.WriteLine(cell));
Btw: What is the cell action used for?
Furthermore we have a Table source class that looks like this:
internal sealed class TableSource : ReactiveTableViewSource<MyListObject>
{
public TableSource(UITableView tableView, IReactiveNotifyCollectionChanged<MyListObject> collection, NSString cellKey, float sizeHint, Action<UITableViewCell> initializeCellAction = null) : base(tableView, collection, cellKey, sizeHint, initializeCellAction)
In my view model I have a service that is updating my ReactiveList. It looks like this:
public sealed class MyService : ReactiveObject, IMyService
{
public IReactiveList<MyListObject> MyReactiveList { get; }
public async Task UpdateMyReactiveListAsync() {//...}
Where do I bind the table source to the ReactiveList? Where do I subscribe for events? Is there any documentation or example code I might have missed?
回答1:
Working with ReactiveTableViewSource is quite easy:
Just connect the List with your UITableView
var tableView = new UITableView ();
// Bind the List agains the table view
// SampleObject is our model and SampleCell the cell
ViewModel.WhenAnyValue (vm => vm.Items).BindTo<SampleObject, SampleCell> (tableView, 46, cell => cell.Initialize());
Then create a custom cell where you bind the model data against the cell.
public class SampleCell : ReactiveTableViewCell, IViewFor<SampleObject>
{
public SampleCell () : base() { }
public SampleCell (IntPtr handle) : base(handle) { }
private SampleObject _viewModel;
public SampleObject ViewModel
{
get { return _viewModel; }
set { this.RaiseAndSetIfChanged (ref _viewModel, value); }
}
object IViewFor.ViewModel
{
get { return ViewModel; }
set { ViewModel = value as SampleObject; }
}
public void Initialize()
{
this.WhenAnyValue (v => v.ViewModel.Name).BindTo (
this,
v => v.TextLabel.Text);
}
}
A compelling example you can find here: https://github.com/reicheltp/ReactiveTableViewSource-Sample
Update 2016/03/09: Better do binding in a separated Initialize method to prevent multiple calls.
If you have more questions you can ask me on twitter: @reicheltp
回答2:
May be you miss this https://github.com/reactiveui/ReactiveUI/blob/275eca3dc2e5fc93bddb137e60be32885f788688/docs/basics/rx-cocoa-delegates.md
To subscribe the event you can assign a UITableViewDelegateRx
var tvd = new UITableViewDelegateRx ();
Table.Delegate = tvd;
tvd.RowSelectedObs.Subscribe (c => { });
回答3:
Just to make a small update using ReactiveUI version 9.16.6:
Example that lists some cities with theres according postal codes.
- => My ViewController inherites from ReactiveViewController
- => My CityViewModel inherites from ReactiveObject
- => My CityItemViewModel inherites from ReactiveObject
=> My CityTableViewCell inherites from ReactiveTableViewCell
public class CityViewModel : ReactiveObject { private ICityService CityService { get; } private SourceCache<CityItemViewModel, int> _citiesCache; private IObservable<IChangeSet<CityItemViewModel, int>> _citiesOperations => _citiesCache.Connect(); public readonly ReadOnlyObservableCollection<CityItemViewModel> Cities; public CityViewModel(ICityService cityService) { CityService = cityService; } #region CityViewModelCommand public ReactiveCommand<CityItemViewModel, Unit> CityClickCommand { get; } #endregion private async Task<IEnumerable<CityItemViewModel>> SearchCitiesAsync(string searchText, CancellationToken token) { IEnumerable<CityItemViewModel> items; if (string.IsNullOrEmpty(searchText)) items = await CityService.ToListWithCountryCodeAsync(cancellationToken: token); else items = await CityService.ToListBySearchWithCountryCodeAsync(searchText, cancellationToken: token); _citiesCache.Edit(innerList => { innerList.Clear(); innerList.AddOrUpdate(items); }); return items; } } public class CityItemViewModel : ReactiveObject { public int Id { get; set; } public string Name { get; set; } public string PostalCode { get; set; } public override string ToString() => $"[CityItemViewModel: Id={Id}, Name={Name}, PostalCode={PostalCode}]"; public CityItemViewModel() : base() { } }
In my ViewController's ViewDidLoad method:
this.WhenActivated((cleanup) =>
{
this.Bind(ViewModel,
x => x.SearchText,
x => x.searchBar.Text)
.DisposeWith(cleanup);
this.WhenAnyValue(v => v.ViewModel.Cities)
.BindTo<CityItemViewModel, CityTableViewCell>(tableView, CityTableViewCell.Key, 50, cell => cell.Initialize(),
source => source.ElementSelected.InvokeCommand(ViewModel.CityClickCommand))
.DisposeWith(cleanup);
});
In my Initialize method of my UITableViewCell:
public void Initialize()
{
this.WhenAnyValue(v => v.ViewModel.Name)
.Subscribe(name => cityLabel.Text = name);
this.WhenAnyValue(v => v.ViewModel.PostalCode)
.Subscribe(x => postalCodeLabel.Text = x);
}
Hope that can help someone ;)
来源:https://stackoverflow.com/questions/35750732/how-does-reactivetableviewsourcetsource-work