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"
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
'
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.
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
1 Answers
Answers 1
You can extend ContentPage
to create a generic type - that supports type parameter for view-model - which in turn can be used in Binding
markup extension.
Although it may not give you intellisense 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)); } }
Sample usage
<?xml version="1.0" encoding="utf-8"?> <l:ContentPage ... xmlns:l="clr-namespace:SampleApp" x:TypeArguments="l:ThisPageViewModel" x:Name="This" x:Class="SampleApp.SampleAppPage"> ... <Label Text="{Binding ViewModel.PropA, Source={x:Reference This}}" /> ... </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" }; }
0 comments:
Post a Comment