I am working on a .Net Maui app where I have to use radio buttons to allow user to select an item from a list. This list is again a part of another list. The overall structure is like Item1(SubItem1,SubItem2,SubItem3,SubItem4), Item2((SubItem1,SubItem2,SubItem3,SubItem4), Item3((SubItem1,SubItem2,SubItem3,SubItem4), Item4((SubItem1,SubItem2,SubItem3,SubItem4)).
The itemsource (SubItem1,SubItem2,SubItem3,SubItem4) is same for all. I am using BindableLayout and ContentView to generate the view. I am sharing code issue reproduced in a sample app.
My requirement is to select a single SubItem for an Item and its value should be stored in a bindable property SelectedItem.
The issue I am facing is whenever I select a subitem say SubItem1 for a Item1 then selected item in all the remaining list Item2,Item3,Item4 also get updated with SubItem1.
I tried by making GroupName different for radio buttons and binding it. This is causing the issue that if I select one of the radio button then RadioButton_CheckedChanged event is called 4 times.
I have to avoid mutiple calls here.
Can someone suggest where I am doing wrong?
Code for BindableLayout MainPage
<ContentPage
xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="MauiSampleApp.Views.RadioButtonPage"
xmlns:controls="clr-namespace:MauiSampleApp.Controls"
Title="RadioButtonPage">
<VerticalStackLayout
Spacing="10">
<VerticalStackLayout
BindableLayout.ItemsSource="{Binding ModelItemList}">
<BindableLayout.ItemTemplate>
<DataTemplate>
<Grid
RowSpacing="6"
RowDefinitions="20,auto">
<Label
Grid.Row="0"
Text="{Binding Title}"/>
<controls:RadioButtonContentView
Grid.Row="1"
GroupName="{Binding Title}"
ItemSource="{Binding ItemList}"/>
</Grid>
</DataTemplate>
</BindableLayout.ItemTemplate>
</VerticalStackLayout>
</VerticalStackLayout>
</ContentPage>
Code Behind for MainPage
public partial class RadioButtonPage : ContentPage
{
private RadioButtonViewModel viewModel { get; set; }
public RadioButtonPage()
{
InitializeComponent();
BindingContext = viewModel = new RadioButtonViewModel();
}
}
ViewModel for MainPage
public class RadioButtonViewModel : ObservableObject
{
private List<ModelClass> _modelItemList = new();
public List<ModelClass> ModelItemList
{
get => _modelItemList;
set => SetProperty(ref _modelItemList, value);
}
private PickerItem _selectedItem;
public PickerItem SelectedItem
{
get => _selectedItem;
set => SetProperty(ref _selectedItem, value);
}
public List<PickerItem> RadioButtonList { get; set; }
public RadioButtonViewModel()
{
RadioButtonList = new()
{
new PickerItem { Key = "SubItem1", IsSelected = true },
new PickerItem { Key = "SubItem2", IsSelected = false },
new PickerItem { Key = "SubItem3", IsSelected = false },
new PickerItem { Key = "SubItem4", IsSelected = false }
};
ModelItemList = new()
{
new ModelClass { Title = "Item1", ItemList = RadioButtonList, SelectedItem = RadioButtonList[0] },
new ModelClass { Title = "Item2", ItemList = RadioButtonList, SelectedItem = RadioButtonList[1] },
new ModelClass { Title = "Item3", ItemList = RadioButtonList, SelectedItem = RadioButtonList[2] },
new ModelClass { Title = "Item4", ItemList = RadioButtonList, SelectedItem = RadioButtonList[3] }
};
}
}
Models used:
- ModelClass
public class ModelClass : ObservableObject
{
private string _title = "";
public string Title
{
get => _title;
set => SetProperty(ref _title, value);
}
private List<PickerItem> _itemList = new();
public List<PickerItem> ItemList
{
get => _itemList;
set => SetProperty(ref _itemList, value);
}
private PickerItem _selectedItem;
public PickerItem SelectedItem
{
get => _selectedItem;
set => SetProperty(ref _selectedItem, value);
}
}
- PickerItem
public class PickerItem : ObservableObject
{
private string _key = "";
public string Key
{
get => _key;
set => SetProperty(ref _key, value);
}
private bool _isSelected = false;
public bool IsSelected
{
get => _isSelected;
set => SetProperty(ref _isSelected, value);
}
}
Code for RadioButtonContentView:
<ContentView xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="MauiSampleApp.Controls.RadioButtonContentView"
x:Name="this">
<VerticalStackLayout
BindableLayout.ItemsSource="{Binding ItemSource, Source={x:Reference this}}"
RadioButtonGroup.GroupName="{Binding GroupName, Source={x:Reference this}}">
<BindableLayout.ItemTemplate>
<DataTemplate>
<Grid
ColumnSpacing="12"
ColumnDefinitions="20,*">
<RadioButton
Grid.Column="0"
IsChecked="{Binding IsSelected}"
Value="{Binding .}"
CheckedChanged="RadioButton_CheckedChanged"/>
<Label
Grid.Column="1"
Text="{Binding Key}"/>
<Grid.GestureRecognizers>
<TapGestureRecognizer
Tapped="TapGestureRecognizer_Tapped"
CommandParameter="{Binding .}"/>
</Grid.GestureRecognizers>
</Grid>
</DataTemplate>
</BindableLayout.ItemTemplate>
</VerticalStackLayout>
</ContentView>
Code behind for RadioButtonContentView
public partial class RadioButtonContentView : ContentView
{
public static readonly BindableProperty SelectedItemProperty = BindableProperty.Create(
nameof(SelectedItem),
typeof(PickerItem),
typeof(RadioButtonContentView),
null,
propertyChanged: SelectedItemPropertyChanged);
public PickerItem SelectedItem
{
get => (PickerItem)GetValue(SelectedItemProperty);
set => SetValue(SelectedItemProperty, value);
}
private static void SelectedItemPropertyChanged(BindableObject bindable, object oldValue, object newValue)
{
if (oldValue is PickerItem oldPickerItem)
{
oldPickerItem.IsSelected = false;
}
if (newValue is PickerItem newPickerItem)
{
newPickerItem.IsSelected = true;
}
}
public static readonly BindableProperty ItemSourceProperty = BindableProperty.Create(
nameof(ItemSource),
typeof(List<PickerItem>),
typeof(RadioButtonContentView),
new List<PickerItem>());
public List<PickerItem> ItemSource
{
get => (List<PickerItem>)GetValue(ItemSourceProperty);
set => SetValue(ItemSourceProperty, value);
}
public static readonly BindableProperty GroupNameProperty = BindableProperty.Create(
nameof(GroupName),
typeof(string),
typeof(RadioButtonContentView),
"");
public string GroupName
{
get => (string)GetValue(GroupNameProperty);
set => SetValue(GroupNameProperty, value);
}
public RadioButtonContentView()
{
InitializeComponent();
}
void RadioButton_CheckedChanged(object sender, CheckedChangedEventArgs e)
{
if (sender is RadioButton radioButton)
{
if (e.Value)
{
var selectedPickerItem = radioButton.Value;
SelectedItem = (PickerItem)selectedPickerItem;
}
}
}
void TapGestureRecognizer_Tapped(object sender, TappedEventArgs e)
{
if (e.Parameter is PickerItem selectedPickerItem)
{
SelectedItem = selectedPickerItem;
}
}
}
Based on the code you shared, I found you have missed property
SelectedItemforModelClass.cswhile initializing the variableModelItemList.So, I added such variable for
ModelClass.cs, just as follows:Besides,we also need to use the different list instance
RadioButtonListforRadioButtonViewModel.cs, just as Nikos said. And theSelectedItemshould be an element of theItemListfor eachModelClassinstance. The way you used was not in the right way.You can refer to the following code:
Note:
I modified the way of setting the selectedItem, you can check the added constructor method of
ModelClass: