I have the following problem, which I cannot comprehend. I'm quite new to Xamarin, and I've been working on this particular problem for quite some time now.
TL;DR: I have an MyGroups[] array, which is used by a Repeater in XAML. The DataTemplate, used by the Repeater, has an ObservableCollection, which is populated in XAML. Binding one of MyGroups's properties with Description.Title does not work, however using the same property outside of the Description Collection works just fine.
Long story:
Let's say we have a Repeater, which is populated by the VM's MyGroups[] property. The Repeater and Models:Group look something like this:
<Grial:Repeater ItemTemplate = "{ DynamicResource GroupDataTemplate }"
ItemsSource = "{ Binding MyGroups }"/>
namespace MyApp.Models
{
public class Group
{
public string MyProperty
{
get;
set;
}
public string MyOtherProperty
{
get;
set;
}
}
}
This Repeater gets the following DataTemplate:
<DataTemplate x:Key = "GroupDataTemplate">
<Cards:CardTemplate x:DataType = "Models:Group">
<StackLayout HorizontalOptions = "Center"
VerticalOptions = "Center">
<Label>
<Label.Text>
<MultiBinding StringFormat="{} {0} {1}"> //<---- this works
<Binding Path = "MyProperty" />
<Binding Source = "{ x:Static Strings:AppResources.MyString }"/>
</MultiBinding>
</Label.Text
</Label>
</StackLayout>
<Cards:CardTemplate.Descriptions>
<Models:Description>
<Models:Description.Title>
<MultiBinding StringFormat="{} {0} {1}"> //<---- this value is always null
<Binding Path = "MyProperty" />
<Binding Source = "{ x:Static Strings:AppResources.MyString }"/>
</MultiBinding>
</Models:Description.Title>
</Models:Description>
<Models:Description>
<Models:Description.Title>
<MultiBinding StringFormat="{} {0} {1}"> //<---- this value is always null
<Binding Path = "MyOtherProperty" />
<Binding Source = "{ x:Static Strings:AppResources.MyString }"/>
</MultiBinding>
</Models:Description.Title>
</Models:Description>
</Cards:CardTemplate.Descriptions>
</Cards:CardTemplate>
</DataTemplate>
The Cards:CardTemplate looks something like this:
<Grial:CardView ...>
...
<Grial:GridView RowSpacing = "16"
Grid.Row = "1"
ColumnCount = "2"
ItemsSource = "{ Binding Source={ RelativeSource AncestorType={ x:Type Cards:CardTemplate } }, Path=Descriptions }">
<Grial:GridView.ItemTemplate>
<DataTemplate x:Name = "x_cardItemsDescriptionTemplate">
<StackLayout Spacing = "5"
Orientation = "Horizontal">
<Label VerticalOptions = "Center"
Text = "{ Binding Source={ RelativeSource AncestorType={ x:Type Models:Description } }, Path=Icon }"/>
<Label VerticalOptions = "Center"
Text = "{ Binding Source={ RelativeSource AncestorType={ x:Type Models:Description } }, Path=Description }"/>
</StackLayout>
</DataTemplate>
</Grial:GridView.ItemTemplate>
</Grial:GridView>
</Grial:CardView>
The Description looks like this
using System;
using Xamarin.Forms;
public class Description
: BindableObject
{
public static readonly BindableProperty IconProperty = BindableProperty.Create( propertyName: nameof( Description.Icon ),
returnType: typeof( string ),
declaringType: typeof( Description ),
defaultBindingMode: BindingMode.TwoWay,
propertyChanged: Description.OnIconChanged );
public static readonly BindableProperty TitleProperty = BindableProperty.Create( propertyName: nameof( Description.Title ),
returnType: typeof( string ),
declaringType: typeof( Description ),
defaultBindingMode: BindingMode.TwoWay,
propertyChanged: Description.OnTitleChanged );
public string Icon
{
get => (string)GetValue( IconProperty );
set => SetValue( property: IconProperty, value: value );
}
public string Title
{
get => (string)GetValue( TitleProperty );
set => SetValue( property: TitleProperty, value: value );
}
private static void OnIconChanged( BindableObject bindable, object old_value, object new_value )
{
try
{
( (Description)bindable ).Icon = new_value as string;
}
catch( Exception e )
{
...
}
}
private static void OnTitleChanged( BindableObject bindable, object old_value, object new_value )
{
try
{
( (Description)bindable ).Title = new_value as string;
}
catch( Exception e )
{
...
}
}
}
What I don't understand is why {Binding Path=MyProperty} works for the Label, but not for the Description. The problem is not the CardTemplate, because every hard-coded or resource-bound string gets assigned to the appropriate element. The only problem is the binding itself!
Things I've tried:
- BindingList instead of ObservableCollections
- Description inherits from INotifyPropertyChanged/BindableObject
- Assigning "Source={ RelativeSource AncestorType={ x:Type Models:Group } }" with or without levels
- {Binding .MyProperty}, {Binding Models:Groups.MyProperty}, {Binding /MyProperty}
- Implementing INotifyPropertyChanged in Group
Nothing seems to work, and I cannot understand why? Why does the Label.Text has its value set correctly, but Description.Title not. Is there something that I'm missing?