4

假设我有一个包含属性“注释”的 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; }
}

然后我有一个 ViewModel:

public class SiteViewModel : MvxViewModel
{

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

我还有一个转换器,可以将 SiteItem 转换为 MKAnnotation

所以我想我的问题是我如何绑定这样的东西,因为我们不能直接绑定到“注释”属性?我是否绑定到命令?

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

4

1 回答 1

6

订阅更改集合是数据绑定的基石之一,它依赖于对INotifyCollectionChanged接口的一点了解。

在 MvvmCross 源代码中,有一些示例类展示了如何订阅集合及其更改通知 - 例如Droid 中的MvxViewGroupExtensions.cs和 Touch中的MvxTableViewSource.cs

该技术的核心是创建一个AdapterorSource对象,它侦听整个列表或列表部分的变化,并采取相应的行动。

相同类型的方法适用于具有多个标记的地图,尽管我们还没有任何辅助类。


在没有实际使用 Mac 或 iOS 设备的情况下,以下是我创建包装器的大致步骤......

假设我有一个 Model 对象,例如:

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映射中的更改到地图上显示的注释中的更改的转换。

为此,我们将提供管理器方法:

  1. 创建单个注释:

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

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

    private void RemoveAnnotationFor(House house)
    {
        var annotation = _annotations[house];
        _mapView.RemoveAnnotation(annotation);
        _annotations.Remove(house);
    }
    
  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);
        }
    }
    
  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();
        }
    }
    
  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);
        }
    }
    

写完这一切后,您的 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 等方法的覆盖来完成。

于 2013-07-24T13:06:53.140 回答