我正在开发一个 .Net Maui 应用程序,我必须使用单选按钮来允许用户从列表中选择一个项目。该列表又是另一个列表的一部分。整体结构类似于 Item1(SubItem1,SubItem2,SubItem3,SubItem4), Item2((SubItem1,SubItem2,SubItem3,SubItem4), Item3((SubItem1,SubItem2,SubItem3,SubItem4), Item4((SubItem1,SubItem2,SubItem3,SubItem4) )).
项目源(SubItem1、SubItem2、SubItem3、SubItem4)对于所有项目都是相同的。我正在使用 BindableLayout 和 ContentView 来生成视图。我正在分享示例应用程序中重现的代码问题。 我的要求是为项目选择单个子项目,并且其值应存储在可绑定属性中
SelectedItem
。
我面临的问题是,每当我为 Item1 选择一个子项目(例如 SubItem1),然后在所有剩余列表 Item2、Item3、Item4 中选择的项目也会使用 SubItem1 进行更新。 我尝试通过使单选按钮的 GroupName 不同并绑定它。这导致了以下问题:如果我选择其中一个单选按钮,则
RadioButton_CheckedChanged
事件将被调用 4 次。
我必须避免在这里多次调用。
有人可以建议我哪里做错了吗?
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>
主页的代码隐藏
public partial class RadioButtonPage : ContentPage
{
private RadioButtonViewModel viewModel { get; set; }
public RadioButtonPage()
{
InitializeComponent();
BindingContext = viewModel = new RadioButtonViewModel();
}
}
主页的视图模型
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] }
};
}
}
使用型号:
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);
}
}
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);
}
}
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>
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;
}
}
}
在此代码中,您使用 RadioButtonViewModel 中的相同列表实例 RadioButtonList。
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] }
};
要解决此问题,您需要确保每个 ModelClass 实例都有自己的 PickerItems 列表副本。这样,更改一组中选定的单选按钮不会影响其他组。以下是如何调整 ViewModel 为每个 ModelClass 创建单独的 PickerItem 实例:
public RadioButtonViewModel()
{
ModelItemList = new List<ModelClass>
{
new ModelClass { Title = "Item1", ItemList = GetNewRadioButtonList(), SelectedItem = RadioButtonList[0] },
new ModelClass { Title = "Item2", ItemList = GetNewRadioButtonList(), SelectedItem = RadioButtonList[1] },
new ModelClass { Title = "Item3", ItemList = GetNewRadioButtonList(), SelectedItem = RadioButtonList[2] },
new ModelClass { Title = "Item4", ItemList = GetNewRadioButtonList(), SelectedItem = RadioButtonList[3] }
};
}
private List<PickerItem> GetNewRadioButtonList()
{
return new List<PickerItem>
{
new PickerItem { Key = "SubItem1", IsSelected = false },
new PickerItem { Key = "SubItem2", IsSelected = false },
new PickerItem { Key = "SubItem3", IsSelected = false },
new PickerItem { Key = "SubItem4", IsSelected = false }
};
}