I have been playing with the new Bahavior class in 1.3 and discovered a bug when using Binding on a BindableProperty. I created the following behavior to allow an ItemsSource and an ItemTemplate to be attached to a StackLayout.
public class ItemsSourceBehavior : Behavior<StackLayout>
{
private StackLayout _stackLayout;
public static readonly BindableProperty ItemsSourceProperty =
BindableProperty.Create<ItemsSourceBehavior, IEnumerable>(p => p.ItemsSource, null, BindingMode.Default, null, ItemsSourceChanged);
public IEnumerable ItemsSource
{
get { return (IEnumerable)GetValue(ItemsSourceProperty); }
set { SetValue(ItemsSourceProperty, value); }
}
public static readonly BindableProperty ItemTemplateProperty =
BindableProperty.Create<ItemsSourceBehavior, DataTemplate>(p => p.ItemTemplate, null);
public DataTemplate ItemTemplate
{
get { return (DataTemplate)GetValue(ItemTemplateProperty); }
set { SetValue(ItemTemplateProperty, value); }
}
private static void ItemsSourceChanged(BindableObject bindable, IEnumerable oldValue, IEnumerable newValue)
{
var behavior = bindable as ItemsSourceBehavior;
behavior.SetItems();
}
private void SetItems()
{
_stackLayout.Children.Clear();
if (ItemsSource == null)
return;
foreach (var item in ItemsSource)
_stackLayout.Children.Add(GetItemView(item));
}
private View GetItemView(object item)
{
var content = ItemTemplate.CreateContent();
var view = content as View;
view.BindingContext = item;
return view;
}
protected override void OnAttachedTo(StackLayout bindable)
{
base.OnAttachedTo(bindable);
_stackLayout = bindable;
}
}
However the BindableProperties never get set when using a Binding expression in Xaml like this.
<ScrollView Orientation="Horizontal">
<StackLayout Orientation="Horizontal">
<StackLayout.Behaviors>
<behaviors:ItemsSourceBehavior ItemsSource="{Binding WeatherPeriods}" ItemTemplate="{StaticResource itemItemplate}" />
</StackLayout.Behaviors>
</StackLayout>
</ScrollView>
Notice here that ItemsSource uses a Binding expression and ItemTemplate uses a DataTemplate as a StaticResource. The ItemTemplate does get set but the ItemSource does not. I discovered that the BindingContext on the Behavior never gets set and therefore the ItemsSource is unable to evaluate the BindingExpression. A workaround for this is to set the BindingContext of the Behavior to the BindingContext of the StackLayout when its BindingContext changes like this:
protected override void OnAttachedTo(StackLayout bindable)
{
base.OnAttachedTo(bindable);
_stackLayout = bindable;
bindable.BindingContextChanged += (sender, e) =>
BindingContext = _stackLayout.BindingContext;
}
Now ItemsSourceChanged gets called as a result of the BindingContext being set.
Please let me know if there is a better solution to this.