Tho*_*ler 8 xaml dictionary mvvm xamarin.forms
我正在使用显示地图的页面处理Xamarin.Forms应用程序.XAML是:
<maps:Map x:Name="Map">
...
</maps:Map>
Run Code Online (Sandbox Code Playgroud)
我知道可以从页面的代码隐藏中访问地图,如下所示:
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
});
Run Code Online (Sandbox Code Playgroud)
但是因为这段代码会打破应用程序的MVVM架构,所以我更喜欢Map从我的ViewModel 访问该对象,而不是直接从View /页面访问 - 直接使用它,就像在上面的代码中一样,或者通过数据绑定到它的属性.
有人知道如何做到这一点吗?
如何创建一个新的 Control 说它BindableMap继承自 Map 并执行原始Map内部缺少的绑定更新。实现非常简单,我已经包含了 2 个基本需求;该Pins属性和当前MapSpan。显然,您可以将自己的特殊需求添加到该控件中。之后您要做的就是将 type 属性添加ObservableCollection<Pin>到您的 ViewModel 并将其绑定到您BindableMap在 XAML 中的 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);
}
}
Run Code Online (Sandbox Code Playgroud)
笔记:
Pins向可绑定PinsSource属性添加成员时更新我们的原始属性,我声明了PinsSourceasObservableCollection<Pin>并订阅了它的CollectionChanged事件。显然,IList如果您打算只更改绑定属性的整个值,则可以将其定义为 an 。关于这个问题的第 2 个答案,我的最后一句话是:
尽管将 View 控件作为 ViewModel 属性可以免除我们在后面的代码中编写业务逻辑的麻烦,但仍然感觉有点 hacky。在我看来,MVVM 的 VM 部分的重点(嗯,至少是其中的一个关键点)是它与 V 完全分离和解耦。 而上述答案中提供的解决方案实际上是这样的:
将视图控件插入到您的 ViewModel 的核心。
我认为这样,不仅你打破了 MVVM 模式,而且你也伤了它的心!
小智 8
如果您不想破坏MVVM模式并且仍然能够从ViewModel访问Map对象,则可以使用ViewModel的属性公开Map实例,并从View绑定到该实例。
您的代码的结构应如下文所述。
ViewModel:
using Xamarin.Forms.Maps;
namespace YourApp.ViewModels
{
public class MapViewModel
{
public MapViewModel()
{
Map = new Map();
}
public Map Map { get; private set; }
}
}
Run Code Online (Sandbox Code Playgroud)
视图(在此示例中,我使用a,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>
Run Code Online (Sandbox Code Playgroud)
我没有显示如何操作,但是上面的代码片段只有在ViewModel是BindingContext视图的视图时才有效。
我不认为Pins是 上的可绑定属性Map,您可能需要在 Xamarin 的Uservoice或此处的论坛上提交功能请求: http: //forums.xamarin.com/discussion/31273/