我正在使用显示地图的页面处理Xamarin.Forms应用。 XAML是:
<maps:Map x:Name="Map">
...
</maps:Map>
我知道可以从页面的代码隐藏中访问地图,如下所示:
var position = new Position(37.79762, -122.40181);
Map.MoveToRegion(new MapSpan(position, 0.01, 0.01));
Map.Pins.Add(new Pin
{
Label = "Xamarin",
Position = position
});
但是因为这段代码会破坏应用程序的MVVM架构,所以我更愿意从我的ViewModel访问Map
对象,而不是直接从View /页面访问 - 要么直接使用它,就像在上面的代码中一样,或者通过数据绑定到它的属性。
有人知道如何做到这一点吗?
答案 0 :(得分:5)
如果您不想破坏MVVM模式并且仍然能够从ViewModel访问您的Map对象,那么您可以使用ViewModel中的属性公开Map实例,并从View中绑定它。 / p>
您的代码的结构应如下所述。
ViewModel:
using Xamarin.Forms.Maps;
namespace YourApp.ViewModels
{
public class MapViewModel
{
public MapViewModel()
{
Map = new Map();
}
public Map Map { get; private set; }
}
}
视图(在此示例中,我使用ContentPage
,但您可以使用您喜欢的任何内容):
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="YourApp.Views.MapView">
<ContentPage.Content>
<!--The map-->
<ContentView Content="{Binding Map}"/>
</ContentPage.Content>
</ContentPage>
我没有说明如何,但上面的代码剪辑只能在ViewModel是您视图的BindingContext
时才能生效。
答案 1 :(得分:4)
我有两个选项对我有用,可以帮到你。
您可以向Xamarin.Forms.Maps Map
添加静态ViewModel
属性,并在View实例化期间设置绑定上下文后设置此静态属性,如下所示:
public MapsPage()
{
InitializeComponent();
BindingContext = new MapViewModel();
MapViewModel.Map = MyMap;
}
这将允许您在ViewModel中访问您的地图。
您可以在绑定过程中将地图从视图传递到ViewModel,例如:
<maps:Map
x:Name="MyMap"
IsShowingUser="true"
MapType="Hybrid" />
<StackLayout Orientation="Horizontal" HorizontalOptions="Center">
<Button x:Name="HybridButton" Command="{Binding MapToHybridViewChangeCommand}"
CommandParameter="{x:Reference MyMap}"
Text="Hybrid" HorizontalOptions="Center" VerticalOptions="Center" Margin="5"/>`
从ViewModel的命令中获取Map。
答案 2 :(得分:2)
关于创建一个新控件,说BindableMap
,该控件从Map继承并执行内部Map
原始的绑定更新。实现非常简单,我已经包括了2个基本需求。 Pins
属性和当前MapSpan
。显然,您可以向此控件添加自己的特殊需求。之后,您要做的就是将类型ObservableCollection<Pin>
的属性添加到ViewModel并将其绑定到XAML中BindableMap
的PinsSource属性。
这里是BindableMap:
public class BindableMap : Map
{
public BindableMap()
{
PinsSource = new ObservableCollection<Pin>();
PinsSource.CollectionChanged += PinsSourceOnCollectionChanged;
}
public ObservableCollection<Pin> PinsSource
{
get { return (ObservableCollection<Pin>)GetValue(PinsSourceProperty); }
set { SetValue(PinsSourceProperty, value); }
}
public static readonly BindableProperty PinsSourceProperty = BindableProperty.Create(
propertyName: "PinsSource",
returnType: typeof(ObservableCollection<Pin>),
declaringType: typeof(BindableMap),
defaultValue: null,
defaultBindingMode: BindingMode.TwoWay,
validateValue: null,
propertyChanged: PinsSourcePropertyChanged);
public MapSpan MapSpan
{
get { return (MapSpan)GetValue(MapSpanProperty); }
set { SetValue(MapSpanProperty, value); }
}
public static readonly BindableProperty MapSpanProperty = BindableProperty.Create(
propertyName: "MapSpan",
returnType: typeof(MapSpan),
declaringType: typeof(BindableMap),
defaultValue: null,
defaultBindingMode: BindingMode.TwoWay,
validateValue: null,
propertyChanged: MapSpanPropertyChanged);
private static void MapSpanPropertyChanged(BindableObject bindable, object oldValue, object newValue)
{
var thisInstance = bindable as BindableMap;
var newMapSpan = newValue as MapSpan;
thisInstance?.MoveToRegion(newMapSpan);
}
private static void PinsSourcePropertyChanged(BindableObject bindable, object oldvalue, object newValue)
{
var thisInstance = bindable as BindableMap;
var newPinsSource = newValue as ObservableCollection<Pin>;
if (thisInstance == null ||
newPinsSource == null)
return;
UpdatePinsSource(thisInstance, newPinsSource);
}
private void PinsSourceOnCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
UpdatePinsSource(this, sender as IEnumerable<Pin>);
}
private static void UpdatePinsSource(Map bindableMap, IEnumerable<Pin> newSource)
{
bindableMap.Pins.Clear();
foreach (var pin in newSource)
bindableMap.Pins.Add(pin);
}
}
注意:
Pins
属性时更新原始的PinsSource
属性,我将PinsSource
声明为ObservableCollection<Pin>
并订阅了{ {1}}事件。显然,如果您只想更改绑定属性的整个值,则可以将其定义为CollectionChanged
。关于此问题的2个最先答案的我的最后决定:
尽管将View控件作为ViewModel属性可以使我们免于在后面的代码中编写业务逻辑,但是仍然感到有些hacky。我认为,MVVM的VM部分(至少是其中的关键点)的全部要点是,它与V完全分离并分离。而上述答案中提供的解决方案实际上是这样的:
将View控件插入ViewModel的心脏。
我认为,不仅破坏了MVVM模式,而且破坏了它的心脏!
答案 3 :(得分:1)
我认为Pins
不是Map
上的可绑定属性,您可能希望在Xamarin的Uservoice或此处的四分之一处提交功能请求:http://forums.xamarin.com/discussion/31273/
答案 4 :(得分:0)
这不是理想的,但您可以在后面的代码中侦听属性更改事件,然后从那里应用更改。它有点手册,但它是可行的。
((ViewModels.YourViewModel)BindingContext).PropertyChanged += yourPropertyChanged;
然后定义“yourPropertyChanged”方法
private void yourPropertyChanged(object sender, PropertyChangedEventArgs e)
{
if(e.PropertyName == "YourPropertyName")
{
var position = new Position(37.79762, -122.40181);
Map.MoveToRegion(new MapSpan(position, 0.01, 0.01));
Map.Pins.Add(new Pin
{
Label = "Xamarin",
Position = position
});
}
}