Reputation: 5894
I've got an issue with Xamarin.Forms, which as those who use it know is very similar to WPF. Unfortunately i'm running into an issue i never experienced with WPF.
Scenario is the following:
My Viewmodel:
public class ShellViewModel : ViewModelBase
{
public ShellViewModel()
{
ActiveViewModel = new ProjectActionOverviewViewModel();
}
private ShellViewChildModel _activeViewModel;
public ShellViewChildModel ActiveViewModel
{
get { return _activeViewModel; }
set
{
Debug.WriteLine("Updating ActiveViewModel.");
SetValue(ref _activeViewModel, value);
Task.Run(() => value.ActivateAsync());
Debug.WriteLine("Updated ActiveViewModel.");
}
}
Xaml:
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:viewModels="clr-namespace:MyNamespace.XamarinForms.ViewModels;assembly=MyNamespace.XamarinForms"
xmlns:views="clr-namespace:MyNamespace.XamarinForms.Views;assembly=MyNamespace.XamarinForms"
xmlns:resources="clr-namespace:MyNamespace.XamarinForms.Resources;assembly=MyNamespace.XamarinForms"
xmlns:custom="clr-namespace:MyNamespace.XamarinForms.Views.Custom;assembly=MyNamespace.XamarinForms"
x:Class="MyNamespace.XamarinForms.Views.Pages.ShellView">
<ContentPage.BindingContext>
<viewModels:ShellViewModel></viewModels:ShellViewModel>
</ContentPage.BindingContext>
<Grid BackgroundColor="{x:Static resources:Colors.DefaultBackground}">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Grid Grid.Row="0" BackgroundColor="{x:Static resources:Colors.CiRed}" Padding="14" IsVisible="{Binding ActiveViewModel.IsHeaderVisible}">
<Label TextColor="{x:Static resources:Colors.BrightForeground}" FontSize="20" Text="{Binding ActiveViewModel.HeaderText}"/>
</Grid>
<Grid Grid.Row="1" BackgroundColor="{x:Static resources:Colors.DefaultBackground}">
<custom:ShellChildPresenter ViewModel="{Binding Path=ActiveViewModel}"></custom:ShellChildPresenter>
</Grid>
<Grid IsVisible="{Binding ActiveViewModel.IsBusy}" Grid.Row="0" Grid.RowSpan="2" BackgroundColor="{x:Static resources:Colors.DarkBackground}" Opacity="0.5">
<ActivityIndicator IsRunning="{Binding ActiveViewModel.IsBusy}" VerticalOptions="Center" HorizontalOptions="Center"></ActivityIndicator>
</Grid>
</Grid>
</ContentPage>
My custom control:
public class ShellChildPresenter : ContentView
{
public ShellChildPresenter()
{
}
private static readonly BindableProperty ViewModelProperty = BindableProperty.Create<ShellChildPresenter, ShellViewChildModel>(
presenter => presenter.ViewModel,
defaultValue: null,
defaultBindingMode: BindingMode.OneWay,
propertyChanged: ViewModelPropertyChanged);
private static void ViewModelPropertyChanged(BindableObject bindable, ShellViewChildModel oldValue, ShellViewChildModel newValue)
{
if (newValue != null)
{
var current = bindable as ShellChildPresenter;
if (current == null)
return;
var view = newValue.CreateContentView();
view.BindingContext = newValue;
current.Content = view;
}
}
public ShellViewChildModel ViewModel
{
get { return (ShellViewChildModel)GetValue(ViewModelProperty); }
set { SetValue(ViewModelProperty, value); }
}
}
At first this kept throwing an exception announcing a type mismatch for the "ViewModel" Property. In order to try fix this issue i change the property type to "object" at which point the setter in ShellViewModel.ViewModel received an instance of Xamarin.Forms.Binding.
Can anyone spot something i've done wrong, which explains why i'm getting an instance of Binding instead of ShellViewChildModel? Oddly enough for the other controls in my xaml i'm getting my content displayed just fine.
Any ideas welcome. I'm starting to run out of ideas and docs don't point out any obvious flaws with my code so far :/
Upvotes: 0
Views: 1422
Reputation: 1196
I HATE MAGIC. Seems the "easier" programming becomes the more "MAGIC" stuff happens. More times then not it just ends up screwing me over. Sorry brief commentary here before the Answer.
Change
private static readonly BindableProperty ViewModelProperty
To
public static readonly BindableProperty ViewModelProperty
Why?
The assignment of
ShellChildPresenter ViewModel="{Binding Path=ActiveViewModel}"
is interpreted by the Xaml Parser. First it creates a binding element as described by "{Binding Path=ActiveViewModel}". Second it appends the word Property to the end and scans through the object in Binding Context looking for .....Property to magically wire it all up to.
since your ........Property is private the parser can't see it. So it moves on and does what you told it to do ViewModel=Binding instead of what you intended ViewModelProperty=Binding.
Like I said I HATE MAGIC.
Upvotes: 1