While approaching Xamarin forms cross platform development, I'm struggling with the definitions of reusable controls.
As a first and very basic example, I've developed a dummy component which looks like this:
<ContentView xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:d="http://xamarin.com/schemas/2014/forms/design"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:vmBase="clr-namespace:TestApp.ViewModels.Base"
mc:Ignorable="d"
vmBase:ViewModelLocator.AutoWireViewModel="True"
x:Class="TestApp.Views.Templates.HashtagContainerTemplateView"
x:Name="this">
<ContentView.Content>
<StackLayout BindingContext="{Reference this}">
<Label Text="{Binding Test}"/>
</StackLayout>
</ContentView.Content>
</ContentView>
Where the code behind is:
using Xamarin.Forms;
using Xamarin.Forms.Xaml;
namespace TestApp.Views.Templates
{
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class HashtagContainerTemplateView : ContentView
{
public static readonly BindableProperty TestProperty = BindableProperty.Create(
propertyName: nameof(Test),
returnType: typeof(string),
declaringType: typeof(HashtagContainerTemplateView),
defaultBindingMode: BindingMode.TwoWay,
defaultValue: "I am the default value",
propertyChanged: TestPropertyChanged);
private static void TestPropertyChanged(BindableObject bindable, object oldValue, object newValue)
{
System.Diagnostics.Debugger.Break(); // This is called only when binding with constant values
}
public string Test
{
get => (string)GetValue(TestProperty);
set
{
SetValue(TestProperty, value);
System.Diagnostics.Debugger.Break(); // This is never called
}
}
public HashtagContainerTemplateView()
{
InitializeComponent();
}
}
}
I am trying to load this view into a page by binding the Test property with a value set by the parent ViewModel as you can see here:
namespace TestApp.ViewModels
{
public class MainViewModel : BaseViewModel
{
private string _testString;
public string TestString
{
get => _testString;
set
{
_testString = value;
RaisePropertyChanged();
}
}
public MainViewModel()
{
}
// This is called by the View Model Locator
public override async Task InitializeAsync(object navigationData)
{
TestString = "I am the binded string";
await base.InitializeAsync(navigationData);
}
}
}
Finally, the View is loaded into the parent Page as this:
<templates:HashtagContainerTemplateView Test="I am a costant string"/><!--This Works-->
<templates:HashtagContainerTemplateView Test="{Binding TestString}"/> <!--Not Working-->
When running the App the labels displayed are: I am a costant string
which is as expected I am the default value
which is the default value for the Property, instead of the one I've passed through binding
After some debugging I realized that the TestPropertyChanged is called only when binding with constant values and the Test setter is never called - see the breakpoints inside the code behind, right above - so I think this is the point...
I know there are many topics like this, even here on SO, but I really can't make it work... I believe there is something really simple I'm missing ...
Final note: I am using the Microsoft eShopOnContainers project as a reference, hence I am using the View Model Locator approach. This is why the intialization is not in the ctor but in the InitializeAsync function.
Microsoft itself has a section about Content Views in the documentation, but no bindings are used...
TestString
property to a regular Label, just to check that the view model was correctly wired? Also it's not clear why do you needvmBase:ViewModelLocator.AutoWireViewModel="True"
in your Content View? – Valeriy Kovalenko