Setting the x:DataType of the DataTemplate of a ListView that has a binding to a different object than the current binding context like this
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:viewModels="clr-namespace:DataTypeSourceBug.ViewModels;assembly=DataTypeSourceBug"
x:Class="DataTypeSourceBug.MainPage"
x:Name="Page">
<ContentPage.BindingContext>
<viewModels:MainPageViewModel />
</ContentPage.BindingContext>
<ListView ItemsSource="{Binding StringList}">
<ListView.ItemTemplate>
<DataTemplate x:DataType="x:String">
<ViewCell>
<Label Text="{Binding .}">
<Label.GestureRecognizers>
<TapGestureRecognizer Command="{Binding Path=BindingContext.InsideListViewCommand,Source={x:Reference Page}}"
CommandParameter="{Binding .}" />
</Label.GestureRecognizers>
</Label>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</ContentPage>
produces an error while compiling with the following stack trace
Resource: DataTypeSourceBug.Views.MainPage.xaml...
Creating empty MainPage.__InitComponentRuntime ...done.
Copying body of InitializeComponent to __InitComponentRuntime ...done.
Parsing Xaml... done.
Replacing MainPage.InitializeComponent ()... failed.
Views\MainPage.xaml(17,47): error : Position 17:47. Binding: Property 'BindingContext' not found on 'System.String'
at Xamarin.Forms.Build.Tasks.SetPropertiesVisitor.ParsePath(String path, TypeReference tSourceRef, IXmlLineInfo lineInfo, ModuleDefinition module)
at Xamarin.Forms.Build.Tasks.SetPropertiesVisitor.<CompileBindingPath>d__30.MoveNext()
at Xamarin.Forms.Build.Tasks.SetPropertiesVisitor.<ProvideValue>d__29.MoveNext()
at Xamarin.Forms.Build.Tasks.ILProcessorExtensions.Append(ILProcessor processor, IEnumerable`1 instructions)
at Xamarin.Forms.Build.Tasks.SetPropertiesVisitor.Visit(ElementNode node, INode parentNode)
at Xamarin.Forms.Xaml.ElementNode.Accept(IXamlNodeVisitor visitor, INode parentNode)
at Xamarin.Forms.Xaml.ElementNode.Accept(IXamlNodeVisitor visitor, INode parentNode)
at Xamarin.Forms.Xaml.ElementNode.Accept(IXamlNodeVisitor visitor, INode parentNode)
at Xamarin.Forms.Build.Tasks.SetPropertiesVisitor.SetDataTemplate(IElementNode parentNode, ElementNode node, ILContext parentContext, IXmlLineInfo xmlLineInfo)
at Xamarin.Forms.Build.Tasks.SetPropertiesVisitor.Visit(ElementNode node, INode parentNode)
at Xamarin.Forms.Xaml.ElementNode.Accept(IXamlNodeVisitor visitor, INode parentNode)
at Xamarin.Forms.Xaml.ElementNode.Accept(IXamlNodeVisitor visitor, INode parentNode)
at Xamarin.Forms.Xaml.ElementNode.Accept(IXamlNodeVisitor visitor, INode parentNode)
at Xamarin.Forms.Xaml.RootNode.Accept(IXamlNodeVisitor visitor, INode parentNode)
at Xamarin.Forms.Build.Tasks.XamlCTask.TryCoreCompile(MethodDefinition initComp, MethodDefinition initCompRuntime, ILRootNode rootnode, Exception& exception)
Compiles fine without trying to compile the bindings that use different Source and Path
Error when compiling
as x:Reference are only resolved at runtime, not build time (see related issue), the short-term plan is to disable compilation of bindings with a Source set
Workaround:
Instead of
<StackLayout x:DataType="ViewModels.MyViewModel">
<Label Text="{Binding Title, Source={x:Reference MyView}" />
<Label Text="{Binding TitlePropertyInViewModel" />
</StackLayout>
Use
<StackLayout x:DataType="ViewModels.MyViewModel">
<Label>
<Label.Text>
<Binding Path="Title", Source="{x:Reference MyView}" />
</Label.Text>
</Label>
<Label Text="{Binding TitlePropertyInViewModel" />
</StackLayout>
@StephaneDelcroix That is weird, shouldn't both of these XAMLs compile to the same thing? I mean he is just expanding it to the long form instead of using the Binding markup extension.
The strongly typed bindings use the class TypedBinding instead of Binding, which the binding markup extension understands.
Another workaround:
[ContentProperty(nameof(Path))]
[AcceptEmptyServiceProvider]
public class UnTypedBindingExtension : IMarkupExtension<BindingBase>
{
public virtual string Path { get; set; } = ".";
public BindingMode Mode { get; set; } = BindingMode.Default;
public IValueConverter Converter { get; set; }
public object ConverterParameter { get; set; }
public string StringFormat { get; set; }
public object Source { get; set; }
public string UpdateSourceEventName { get; set; }
public object TargetNullValue { get; set; }
public object FallbackValue { get; set; }
BindingBase IMarkupExtension<BindingBase>.ProvideValue(IServiceProvider serviceProvider)
{
return new Binding(Path, Mode, Converter, ConverterParameter, StringFormat, Source)
{
UpdateSourceEventName = UpdateSourceEventName,
FallbackValue = FallbackValue,
TargetNullValue = TargetNullValue
};
}
object IMarkupExtension.ProvideValue(IServiceProvider serviceProvider)
{
return (this as IMarkupExtension<BindingBase>).ProvideValue(serviceProvider);
}
}
In xaml files:
<StackLayout x:DataType="ViewModels.MyViewModel">
<Label Text="{UnTypedBinding Title, Source={x:Reference MyView}" />
<Label Text="{Binding TitlePropertyInViewModel" />
</StackLayout>
@AmrAlSayed0 yes,, it's strange. @ysmoradi examples should compile the same way... investigating
Most helpful comment
Workaround:
Instead of
Use