I am trying to pass values through properties on a custom content view that I have made. I wanted to have a parent page that is able to declare instances of the custom view, and pass in an ID if needed. I want the ID to be received by the custom view, which should then set the property in the view's view model. Currently my attempts have been unsuccessful, it seems the view model never gets its ID updated. Every time I run it the ID that is shown in the label is an empty guid.
I have the following four classes defined in the project:
My actual application that creates the main page: App
C# (App.xaml.cs)
using ExampleDataBinding.Views;
using Xamarin.Forms;
namespace ExampleDataBinding
{
public partial class App : Application
{
public App()
{
InitializeComponent();
MainPage = new TestMainPage();
}
protected override void OnStart()
{
}
protected override void OnSleep()
{
}
protected override void OnResume()
{
}
}
}
The main page that is shown in my App: TestMainPage
XAML (TestMainPage.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:views="clr-namespace:ExampleDataBinding.Views"
x:Class="ExampleDataBinding.Views.TestMainPage"
x:DataType="views:TestMainPage">
<ContentPage.Content>
<StackLayout>
<Label Text="{Binding MainPageID}" />
<views:TestContentView ArbitraryGuid="{Binding MainPageID}"/>
</StackLayout>
</ContentPage.Content>
</ContentPage>
C# (TestMainPage.xaml.cs)
using System;
using Xamarin.Forms;
using Xamarin.Forms.Xaml;
namespace ExampleDataBinding.Views
{
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class TestMainPage : ContentPage
{
private Guid mainPageID = Guid.NewGuid();
public Guid MainPageID
{
get => mainPageID;
set => mainPageID = value;
}
public TestMainPage()
{
InitializeComponent();
BindingContext = this;
}
}
}
The view that the main page is trying to load: TestContentView
XAML (TestContentView.xaml)
<?xml version="1.0" encoding="UTF-8"?>
<ContentView xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:viewmodels="clr-namespace:ExampleDataBinding.ViewModels"
x:Class="ExampleDataBinding.Views.TestContentView"
x:DataType="viewmodels:TestContentViewModel">
<ContentView.Content>
<Label Text="{Binding Guid}" />
</ContentView.Content>
</ContentView>
C# (TestContentView.xaml.cs)
using ExampleDataBinding.ViewModels;
using System;
using Xamarin.Forms;
using Xamarin.Forms.Xaml;
namespace ExampleDataBinding.Views
{
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class TestContentView : ContentView
{
public static readonly BindableProperty ArbitraryGuidProperty = BindableProperty.Create(nameof(ArbitraryGuid),
typeof(Guid?),
typeof(TestContentView),
null,
BindingMode.TwoWay,
propertyChanged: ArbitraryGuidPropertyChanged);
private static void ArbitraryGuidPropertyChanged(BindableObject bindable, object oldValue, object newValue)
{
var currentTestContentView = bindable as TestContentView;
currentTestContentView.UpdateViewModelGuid();
}
private void UpdateViewModelGuid()
{
if(ArbitraryGuid.HasValue)
viewModel.Guid = ArbitraryGuid.Value;
}
public Guid? ArbitraryGuid
{
get => (Guid?)GetValue(ArbitraryGuidProperty);
set => SetValue(ArbitraryGuidProperty, value);
}
private TestContentViewModel viewModel;
public TestContentView()
{
InitializeComponent();
BindingContext = viewModel = new TestContentViewModel();
}
}
}
The view model for the TestContentView: TestContentViewModel
C# (TestContentViewModel.cs)
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Runtime.CompilerServices;
namespace ExampleDataBinding.ViewModels
{
public class TestContentViewModel : INotifyPropertyChanged
{
private Guid guid = Guid.Empty;
public Guid Guid
{
get => guid;
set => SetProperty(ref guid, value);
}
protected bool SetProperty<T>(ref T backingStore, T value,
[CallerMemberName] string propertyName = "",
Action onChanged = null)
{
if (EqualityComparer<T>.Default.Equals(backingStore, value))
return false;
backingStore = value;
onChanged?.Invoke();
OnPropertyChanged(propertyName);
return true;
}
#region INotifyPropertyChanged
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged([CallerMemberName] string propertyName = "")
{
var changed = PropertyChanged;
if (changed == null)
return;
changed.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
#endregion
}
}
I thought I should be able to have my TestMainPage create a TestContentView and pass in the arbitrary value through the Xaml parameter, but I'm confused as to how it is actually working.
I would like to keep the TestContentView using its own viewmodel that gets populated from a binding or property value if possible.
I may be going in the complete wrong direction here, so any advice is welcome. Thanks for any pointers!
