Cast Binding Path so it recognises ViewModel property at Design-Time

The name of the picture


Cast Binding Path so it recognises ViewModel property at Design-Time



Ok this is more of an annoyance than a problem. There is no error



Page


<ContentPage
...
x:Name="This"
//hack to have typed xaml at design-time
BindingContext="{Binding Source={x:Static viewModels:ViewModelLocator.ChooseTargetLocationVm}}"



SubView


<views:ProductStandardView
...
BindingContext="{Binding Product}">
<Grid.Triggers>
<DataTrigger
Binding="{Binding Path=BindingContext.IsVacate, Source={x:Reference This}}"
TargetType="Grid"
Value="true">
<Setter Property="BackgroundColor" Value="{StaticResource WarningColor}" />
</DataTrigger>
</Grid.Triggers>



When Binding to BindingContext from the Source Reference of This, i get a XAML "warning"


BindingContext


This



Cannot resolve property 'IsVacate' in data context of type 'object'


Binding="{Binding Path=BindingContext.IsVacate, Source={x:Reference This}}"



Obviously the BindingContext is an object and untyped. However the above code compiles and works



What i want to do is cast it, firstly because i have OCD, however mainly because its easy to spot real problems on the IDE page channel bar



The following seems logical but doesn't work


Binding="{Binding Path=BindingContext.(viewModels:ChooseTargetLocationVm.IsVacate),
Source={x:Reference This}}"



In the output i get



[0:] Binding: '(viewModels:ChooseTargetLocationVm' property not
found on
'Inhouse.Mobile.Standard.ViewModels.ChooseTargetLocationVm', target
property: 'Inhouse.Mobile.Standard.Views.ProductStandardView.Bound'


viewModels:ChooseTargetLocationVm


Inhouse.Mobile.Standard.ViewModels.ChooseTargetLocationVm


Inhouse.Mobile.Standard.Views.ProductStandardView.Bound



I understand the error, yet how else would i cast?



And just for stupidity, obviously the following wont compile


Binding="{Binding Path=((viewModels:ChooseTargetLocationVm)BindingContext).IsVacate, Source={x:Reference This}}"



So is there a way to cast a BindingContext to a ViewModel so any SubProperty references are typed at design time?



Update



This is relevant for inside a DataTemplate or in this case when the control has its own BindingContext which is why i need to use the Source={x:Reference This} to target the page.


DataTemplate


BindingContext


Source={x:Reference This}



Note : <ContentPage.BindingContext> doesn't work for me as i'm using prism and unity and it doesn't seem to play with well a default constructor on initial tests, though i might play around with this some more


<ContentPage.BindingContext>



This question has not received enough attention.





How about just setting the binding path to the desired property Path=IsVacate?
– Nkosi
2 days ago


Path=IsVacate





@Nkosi Ahh sorry if i wasn't clear, this is a datatemplate in in a listview, so i need to reference the page and binding to a property in its viewmodel , ill update the question to be more specific
– TheGeneral
2 days ago





Property suggestions are presented (in VS 15.7) with the syntax <ContentPage.BindingContext><viewModels:MyViewModel/></ContentPage.BindingContext>, but probably not members of properties or inside a datatemplate (still in VS 15.7 (blog.xamarin.com/…) )
– Benl
yesterday






1 Answer
1



You can extend ContentPage to create a generic type - that supports typed parameter for view-model - which in turn can be used in Binding markup extension.


ContentPage


Binding



Although it may not give you intelisense like support - but should definitely remove the warning for you.



For e.g.:


/// <summary>
/// Create a base page with generic support
/// </summary>
public class ContentPage<T> : ContentPage
{
/// <summary>
/// This property basically type-casts the BindingContext to expected view-model type
/// </summary>
/// <value>The view model.</value>
public T ViewModel { get { return (BindingContext != null) ? (T)BindingContext : default(T); } }

/// <summary>
/// Ensure ViewModel property change is raised when BindingContext changes
/// </summary>
protected override void OnBindingContextChanged()
{
base.OnBindingContextChanged();

OnPropertyChanged(nameof(ViewModel));
}
}


<?xml version="1.0" encoding="utf-8"?>
<l:ContentPage
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:l="clr-namespace:SampleApp"
x:TypeArguments="l:ThisPageViewModel"
x:Name="This"
x:Class="SampleApp.SampleAppPage">
<Grid Margin="20">
<ListView ItemsSource="{Binding Path=Items}">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<StackLayout>
<Label Text="{Binding ViewModel.PropA, Source={x:Reference This}}" />
<Label Text="{Binding}" />
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Grid>
</l:ContentPage>



Code-behind


public partial class SampleAppPage : ContentPage<ThisPageViewModel>
{
public SampleAppPage()
{
InitializeComponent();

BindingContext = new ThisPageViewModel();
}
}



View model


/// <summary>
/// Just a sample viewmodel with properties
/// </summary>
public class ThisPageViewModel
{
public string PropA { get; } = "PropA";
public string PropB { get; } = "PropB";
public string PropC { get; } = "PropC";

public string Items { get; } = new { "1", "2", "3" };
}






By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

Popular posts from this blog

How to scale/resize CVPixelBufferRef in objective C, iOS

Stripe::AuthenticationError No API key provided. Set your API key using “Stripe.api_key = ”

SVG with two text elements. When one resizes due to textLength - how to resize the other one to the same character size