Подписывание изменениям коллекции является одним из краеугольных камней привязки данных и основывается на небольшом знании интерфейса INotifyCollectionChanged
.
В источнике MvvmCross имеется несколько примеров классов, которые показывают, как подписаться на коллекции и их уведомления об изменениях - например, MvxViewGroupExtensions.cs в Droid и MvxTableViewSource.cs в сенсорный
Ядро в технике заключается в создании Adapter
или Source
объект, который прослушивает изменения либо в целом списке или части списка и который принимает соответствующие меры.
Такой же подход применяется к картам с несколькими маркерами - хотя у нас пока нет вспомогательных классов.
фактически без устройства Mac или IOS под рукой, вот примерно шаги, которые я хотел сделать, чтобы создать оболочку ...
Если предположить, что у меня был объект модели, как:
public class House
{
public double Lat { get; set; }
public double Lng { get; set; }
public string Name { get; set; }
}
Внутри ViewModel как:
public class FirstViewModel : MvxViewModel
{
public ObservableCollection<House> HouseList { get; set; }
}
с этим сделано, то в представлении мы можем создать класс аннотаций для каждого дома - например, что-то вроде:
public class HouseAnnotation : MKAnnotation
{
public HouseAnnotation(House house)
{
// Todo - the details of actually using the house here.
// in theory you could also data-bind to the house too (e.g. if it's location were to move...)
}
public override CLLocationCoordinate2D Coordinate { get; set; }
}
Мы могли бы создать HouseAnnotationManager
, который будет ответственность будет управлять переводом изменений в HouseList
привязывается к изменениям в аннотации, что отображается на карте.
Чтобы сделать это, мы дадим методу менеджера в:
Создания единой аннотации:
private MKAnnotation CreateAnnotation(House house)
{
return new HouseAnnotation(house);
}
Добавить аннотацию к карте (и в локальную таблицу перекодировки)
private void AddAnnotationFor(House house)
{
var annotation = CreateAnnotation(house);
_annotations[house] = annotation;
_mapView.AddAnnotation(annotation);
}
Удалить аннотацию с карты (и из локального поиска таблица)
private void RemoveAnnotationFor(House house)
{
var annotation = _annotations[house];
_mapView.RemoveAnnotation(annotation);
_annotations.Remove(house);
}
сделать то же действия для списков:
private void AddAnnotations(IList newItems)
{
foreach (House house in newItems)
{
AddAnnotationFor(house);
}
}
private void RemoveAnnotations(IList oldItems)
{
foreach (House house in oldItems)
{
RemoveAnnotationFor(house);
}
}
Ответить на INotifyCollection
изменения:
private void OnItemsSourceCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
switch (e.Action)
{
case NotifyCollectionChangedAction.Add:
AddAnnotations(e.NewItems);
break;
case NotifyCollectionChangedAction.Remove:
RemoveAnnotations(e.OldItems);
break;
case NotifyCollectionChangedAction.Replace:
RemoveAnnotations(e.OldItems);
AddAnnotations(e.NewItems);
break;
case NotifyCollectionChangedAction.Move:
// not interested in this
break;
case NotifyCollectionChangedAction.Reset:
ReloadAllAnnotations();
break;
default:
throw new ArgumentOutOfRangeException();
}
}
Ответить на цельных изменения списка:
// MvxSetToNullAfterBinding isn't strictly needed any more
// - but it's nice to have for when binding is torn down
[MvxSetToNullAfterBinding]
public virtual IEnumerable<House> ItemsSource
{
get { return _itemsSource; }
set { SetItemsSource(value); }
}
protected virtual void SetItemsSource(IEnumerable<House> value)
{
if (_itemsSource == value)
return;
if (_subscription != null)
{
_subscription.Dispose();
_subscription = null;
}
_itemsSource = value;
if (_itemsSource != null && !(_itemsSource is IList))
MvxBindingTrace.Trace(MvxTraceLevel.Warning,
"Binding to IEnumerable rather than IList - this can be inefficient, especially for large lists");
ReloadAllAnnotations();
var newObservable = _itemsSource as INotifyCollectionChanged;
if (newObservable != null)
{
_subscription = newObservable.WeakSubscribe(OnItemsSourceCollectionChanged);
}
}
С этим все написано, то ваш ViewModel может иметь частный _manager
поле и может создавать и данных связывать это как:
_manager = new HouseAnnotationManager(myMapView);
var set = this.CreateBindingSet<FirstView, FirstViewModel>();
set.Bind(_manager).To(vm => vm.HouseList);
set.Apply();
В целом, это может выглядеть примерно так: https://gist.github.com/slodge/6070386
Отказ от ответственности: этот код не был скомпилирован, не говоря уже о запуске, но подход в основном правильный (я думаю)
Примечание: если это делает/не работает с некоторой фиксации, я бы очень хотел он представил в сообщество MVX в качестве образца;)
Тот же самый основной подход должен также работать в Android - хотя в Android вам также придется сражаться против установки - Ant, Google Play v2 и всего этого джаза.
Если вы хотите сделать дальнейшие манипуляции с картами - например, изменение центра карты и масштабирование при добавлении дома, то это, очевидно, можно сделать из-за переопределения таких методов, как AddAnnotation внутри вашего менеджера.
Спасибо Stuart! Я внесу некоторые изменения и отчитаюсь. –
Стюарт отлично работал для iOS. Я попробую сторону дроида, но я думаю, как вы сказали, я просто столкнусь с конкретными проблемами Droid. Большое спасибо, отличный ответ! –
Рад, что это сработало. Был бы * любить *, чтобы увидеть ваши эксперименты с картой дроида, где-то где-то в блоге :) – Stuart