I am trying to make a custom ContentView for a control that works similar to Bootstrap's Navigation Pills.
Here is the code for my custom view.
PillToggle.xaml.cs
public partial class PillToggle : ContentView
{
public PillToggle()
{
InitializeComponent();
this.BindingContext = this;
// BindableLayout.SetItemsSource(this.PillsContainer, this.Pills);
}
public ObservableCollection<Pill> Pills
{
get { return (ObservableCollection<Pill>)GetValue(PillsProperty); }
set { SetValue(PillsProperty, value); }
}
public static readonly BindableProperty PillsProperty =
BindableProperty.Create("Pills", typeof(ObservableCollection<Pill>), typeof(PillToggle), defaultValue: new ObservableCollection<Pill>());
}
PillToggle.xaml
<?xml version="1.0" encoding="utf-8" ?>
<ContentView xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:controls="clr-namespace:REDACTED"
x:Class="REDACTED.PillToggle">
<Frame BackgroundColor="#eeeeee" CornerRadius="24" Padding="4" MinimumHeightRequest="48">
<HorizontalStackLayout x:Name="PillsContainer" BindableLayout.ItemsSource="{Binding Pills}" HorizontalOptions="Start" >
<BindableLayout.ItemTemplate>
<DataTemplate x:DataType="controls:Pill">
... snip
</DataTemplate>
</BindableLayout.ItemTemplate>
</HorizontalStackLayout>
</Frame>
</ContentView>
Pill.cs
public partial class Pill : ObservableObject
{
[ObservableProperty]
public string? label;
[ObservableProperty]
private bool selected = false;
}
When I try to use this View in a ContentPage, I am unable to Bind the Pills Collection via XAML.
MyPageViewModel.cs
public partial class MyViewModel : ObservableObject
{
//Hard coded for now, but may become dynamic
public IEnumerable<Pill> MyPills { get; } = new List<Pill>()
{
new Pill() { Label = "Test 1" },
new Pill() { Label = "Test 2" }
};
}
MyPage.xaml (Usage)
<controls:PillToggle x:Name="MyToggle" Pills="{Binding MyPills}" >
MyPage.xaml.cs (Constructor)
public MyPage(MyViewModel viewModel)
{
this.BindingContext = viewModel;
InitializeComponent();
}
When I run my app, I don't see any pills. I can see the Views background color, but the HorizontalStackView appears to not be bound to any items.
If I remove the Binding from Xaml, and manually bind in the code behind constructor, it works.
MyToggle.Pills = new ObservableCollection<Controls.Pill>(viewModel.MyPills);
It also works if I manually add the pills like this.
<controls:PillToggle x:Name="MyToggle">
<controls:PillToggle.Pills>
<controls: Pill Label="Test 3" Selected="True" />
<controls: Pill Label="Test 4" />
</controls:PillToggle.Pills>
</controls:PillToggle>
How can I get Binding to work in Xaml for my custom ContentView?
First of all, and this is my opinion, you should avoid have a bindingcontext in your control. It will make your life so much simpler. The viewmodel you have its bindingcontext inheriting from the ContentPage (View). Also you have mixed a lot of thing and made it much more harder than it is. So let us see how we can make this work.
First we have the ContentView logic, Code behind. You had your BindableProperty a bit wrong. It need to have the actual propertyname as it first parameter. Also the second pararmeter, the return type was wrong, much because you had mixed in ObservableCollection in all this. It just need a List. So the the code is down to:
If we go to the xaml page I have just changed the bindings and added a TapGestureRecognizer because you talked about be able to click on it.
I have made your model much simpler too.:
If we now look at the implementation you have your View with the control
Which binds to MyPills that is in your ViewModel