Sorry if the title is a little vague. I'm creating a custom map that has bindable pins and xaml callouts. I've got the bindable pins pretty much rocking, but my callouts are effed up.
To start, I have a custom callout adapter that get's wired up in the custom renderer.
private void SetupCalloutAdapter()
{
if (_adapter != null)
{
_adapter.Dispose();
_adapter = null;
}
if (FormsMap.CalloutTemplate != null)
{
_adapter = new XfxMapCalloutAdapter(NativeMap, FormsMap.CalloutTemplate, Element);
}
}
This adapter essentially wires up the xaml using CreateContent()
just before the view is to be shown.
public class XfxMapCalloutAdapter : Object, GoogleMap.IInfoWindowAdapter
{
private static readonly Dictionary<int, XfxMarker> BindingContextCollection = new Dictionary<int, XfxMarker>();
private readonly DataTemplate _mapCalloutTemplate;
private readonly Xamarin.Forms.View _parent;
public XfxMapCalloutAdapter(GoogleMap nativeMap, DataTemplate calloutTemplate, Xamarin.Forms.View parent)
{
_mapCalloutTemplate = calloutTemplate;
_parent = parent;
nativeMap.SetInfoWindowAdapter(this);
}
public View GetInfoContents(Marker marker)
{
if (_mapCalloutTemplate == null)
{
return null;
}
int key;
var callout = (XfxMapCallout)_mapCalloutTemplate.CreateContent();
callout.Parent = _parent;
if (int.TryParse(marker.Snippet, out key))
{
callout.BindingContext = BindingContextCollection[key];
//title = BindingContextCollection[key].Title;
}
return new XfxMapCalloutContainer(Android.App.Application.Context, callout, string.Empty);
}
public View GetInfoWindow(Marker marker)
{
// we're not drawing an entire background layout, so we'll just leave this null.
// if we wanted to create our own custom layout, we'd use this.
return null;
}
public void AddToBindingContext(XfxMarker marker)
{
if (!BindingContextCollection.ContainsKey(marker.HashId))
{
BindingContextCollection.Add(marker.HashId, marker);
}
}
public void RemoveFromBindingContext(int hashId)
{
if (BindingContextCollection.ContainsKey(hashId))
{
BindingContextCollection.Remove(hashId);
}
}
}
The view is wrapped in a custom container that handles some measurements and also converts the Xaml view into a native view.
public sealed class XfxMapCalloutContainer : ViewGroup
{
private readonly IVisualElementRenderer _renderer;
public XfxMapCalloutContainer(IntPtr javaReference, JniHandleOwnership transfer)
: base(javaReference, transfer)
{
// this constructor prevents the app from crashing.
}
public XfxMapCalloutContainer(Context context, XfxMapCallout view, string title)
: base(context)
{
// todo: figure out how to get the title in here.
_renderer = Platform.CreateRenderer(view);
Platform.SetRenderer(view, _renderer);
AddView(_renderer.ViewGroup);
view.OnAppearing(this);
}
protected override void OnMeasure(int widthMeasureSpec, int heightMeasureSpec)
{
var sizeRequest = _renderer.Element.GetSizeRequest(double.PositiveInfinity, double.PositiveInfinity);
var width = (int)Context.ToPixels(sizeRequest.Request.Width);
var height = (int)Context.ToPixels(sizeRequest.Request.Height);
SetMeasuredDimension(width, height);
}
protected override void OnLayout(bool changed, int l, int t, int r, int b)
{
_renderer.Element.Layout(new Rectangle(0, 0, r - l, b - t));
_renderer.UpdateLayout();
}
}
What I'm running into is two fold:
- When I update the Bindable Properties on the ViewModel, they don't update on the view (does X.F handle this, or is there more I should be doing?)
- For some stupid reason, my button doesn't show text.
<xfxMap:XfxMapCallout xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:xfxMap="clr-namespace:Xfx.XfxMap;assembly=Xfx.XfxMap"
x:Class="XfxMapExample.ExampleMarkerCallout">
<StackLayout>
<BoxView WidthRequest="20" HeightRequest="30" Color="Blue"/>
<Label Text="Static Text" TextColor="Aqua" />
<Label Text="{Binding Description}" TextColor="Fuchsia" />
<Label x:Name="Counter" Text="{Binding Counter}" TextColor="Green" />
<Button Image="Icon" Text="Foo" TextColor="Pink" BackgroundColor="Maroon" />
</StackLayout>
</xfxMap:XfxMapCallout>
Essentially I'm updating the Counter
on a 1 second loop in hopes of seeing it update on the view, but it never does.... what am I missing?
note: if the data is there from the beginning, it'll show up, just doesn't pick up the property changes.
public async void ExampleInit()
{
while (true)
{
await Task.Delay(1000);
Counter++;
}
}
Image may be NSFW.
Clik here to view.