使用MvvmCross如何将注释列表绑定到MapView?

Mar*_*son 4 mvvmcross xamarin

假设我有一个包含属性"Annotations"的MapView.要在MapView上获取注释,您必须使用AddAnotation或AddAnotations.

public class SiteItems
{
    public string Title { get; set; }
    public string SubTitle { get; set; }

    public string Phone { get; set; }
    public string Address { get; set; }
    public string Url { get; set; }

    public double Latitude { get; set; }
    public double Longitude { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

然后我有一个ViewModel:

public class SiteViewModel : MvxViewModel
{

    private IObservableCollection<Models.SiteItems> _siteItems;
    public IObservableCollection<Models.SiteItems> SiteItems {
        get{ return _siteItems; }
        set{ _siteItems = value; 
            RaisePropertyChanged (() => SiteItems);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

我还有一个将SiteItem转换为MKAnnotation的转换器

所以我想我的问题是如何绑定这样的东西,因为我们不能直接绑定到"Annotations"属性?我绑定命令吗?

谢谢,任何帮助表示赞赏!

Stu*_*art 6

订阅更改集合是Data-Binding的基石之一,并且依赖于对INotifyCollectionChanged界面的一点了解.

内MvvmCross源,还有一些展示如何订阅集合及其变更通知几个示例类-如MvxViewGroupExtensions.cs在Droid和MvxTableViewSource.cs在触摸

该技术的核心是创建一个AdapterSource对象,该对象在整个列表或列表的某些部分中监听更改,并相应地采取操作.

相同类型的方法适用于具有多个但是标记的地图 - 尽管我们还没有任何帮助类.


如果没有真正拥有Mac或iOS设备,这里大致是我创建包装器的步骤......

假设我有一个Model对象:

public class House
{
    public double Lat { get; set; }
    public double Lng { get; set; }
    public string Name { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

在ViewModel里面,如:

public class FirstViewModel : MvxViewModel
{
    public ObservableCollection<House> HouseList { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

完成后,在View中我们可以为每个House创建一个注释类 - 例如:

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; }
}
Run Code Online (Sandbox Code Playgroud)

然后,我们可以创建一个HouseAnnotationManager谁负责管理HouseList映射到地图上显示的注释中的更改的更改的转换.

要做到这一点,我们会给经理方法:

  1. 创建单个注释:

    private MKAnnotation CreateAnnotation(House house)
    {
        return new HouseAnnotation(house);
    }
    
    Run Code Online (Sandbox Code Playgroud)
  2. 向地图添加注释(以及本地查找表)

    private void AddAnnotationFor(House house)
    {
        var annotation = CreateAnnotation(house);
        _annotations[house] = annotation;
        _mapView.AddAnnotation(annotation);
    }
    
    Run Code Online (Sandbox Code Playgroud)
  3. 从地图中删除注释(以及从本地查找表中删除)

    private void RemoveAnnotationFor(House house)
    {
        var annotation = _annotations[house];
        _mapView.RemoveAnnotation(annotation);
        _annotations.Remove(house);
    }
    
    Run Code Online (Sandbox Code Playgroud)
  4. 对列表执行相同的操作:

    private void AddAnnotations(IList newItems)
    {
        foreach (House house in newItems)
        {
            AddAnnotationFor(house);
        }
    }
    
    private void RemoveAnnotations(IList oldItems)
    {
        foreach (House house in oldItems)
        {
            RemoveAnnotationFor(house);
        }
    }
    
    Run Code Online (Sandbox Code Playgroud)
  5. 回应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();
        }
    }
    
    Run Code Online (Sandbox Code Playgroud)
  6. 回应整个列表更改:

    // 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);
        }
    }
    
    Run Code Online (Sandbox Code Playgroud)

完成所有这些后,您的ViewModel可以拥有一个私有_manager字段,并可以创建和数据绑定它:

        _manager = new HouseAnnotationManager(myMapView);

        var set = this.CreateBindingSet<FirstView, FirstViewModel>();
        set.Bind(_manager).To(vm => vm.HouseList);
        set.Apply();
Run Code Online (Sandbox Code Playgroud)

总的来说,这可能看起来像:https://gist.github.com/slodge/6070386

免责声明:此代码尚未编译,更不用说运行了,但这种方法基本上是正确的(我认为)

注意:如果这样做/不适用于某些修复,我非常希望它作为样本提交回Mvx社区;)


同样的基本方法也适用于Android - 虽然在Android中你还必须与设置 - Ant,Google Play v2和所有爵士乐进行斗争.


如果你想进行进一步的地图操作 - 例如在添加房屋时更改地图中心和缩放,那么显然可以在管理器中的方法(例如AddAnnotation)的覆盖范围内完成.