WPF中如何为ListBox内的每个Button触发事件并获取索引?
我需要在包含32个Button的ListBox中实现每个Button点击后在0和1之间切换文本,同时触发事件并获取对应项的索引。目前尝试了两种方案:
方案一:Button绑定Command(无法获取正确索引)
XAML代码
<ListBox x:Name="lbDirection" Grid.Row="1" Grid.Column="1" SelectionMode="Extended" ScrollViewer.HorizontalScrollBarVisibility="Disabled" ScrollViewer.VerticalScrollBarVisibility="Disabled" Margin="30,26,44,83" Style="{StaticResource ListBoxHorz}" ItemContainerStyle="{StaticResource ItemNoBorder}" SelectedItem="{Binding SelectedLineItem}" ItemsSource="{Binding ButtonList}" IsSynchronizedWithCurrentItem="True"> <ListBox.ItemTemplate> <DataTemplate> <Button Style="{StaticResource ChangeButton}" Command="{Binding DataContext.ToggleDirectionCommand, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType= ListBox}}"> <Button.Content> <TextBlock Text="{Binding BtnText}" TextDecorations="Underline" /> </Button.Content> </Button> </DataTemplate> </ListBox.ItemTemplate> </ListBox>
ViewModel代码
public class DigitalIOViewModel : BindableBase { public ICommand ToggleDirectionCommand { get; set; } public ICommand ToggleStateCommand { get; set; } private ObservableCollection<LineButtons> btnlist; private LineButtons _selectedLineItem; public LineButtons SelectedLineItem { get { return _selectedLineItem; } set { _selectedLineItem=value; OnPropertyChanged("SelectedLineItem"); } } public DigitalIOViewModel() { btnlist = new ObservableCollection<LineButtons>(); CreateButtonList(); ToggleDirectionCommand = new RelayCommand(ToggleDirectionAction); } private void ToggleDirectionAction() { LineButtons selectedLineItem = SelectedLineItem; int lineIndex=(SelectedLineItem!= null && ButtonList!=null)? ButtonList.IndexOf(SelectedLineItem) : -1; if (selectedLineItem.BtnText == "1" && lineIndex == 0) { ButtonList[lineIndex] = new LineButtons() { BtnText = "0" }; } else ButtonList[lineIndex] = new LineButtons() { BtnText = "1" }; } public ObservableCollection<LineButtons> ButtonList { get { return btnlist; } set { btnlist = value; OnPropertyChanged("ButtonList"); } } public void CreateButtonList() { for (int i = 0; i < 32; i++) { ButtonList.Add(new LineButtons() { BtnText = "1"}); } } } public class LineButtons : BindableBase { private string btnText; public string BtnText { get { return btnText; } set { btnText = value; OnPropertyChanged("BtnText"); } } }
问题说明
点击Button时无法正确获取SelectedItem或SelectedIndex,导致无法定位到被触发切换的Button对应的项。
方案二:考虑结合IsSelected属性
疑惑点:Button的Command绑定和ListBox的IsSelected属性是否冲突?能否同时使用?
最优解决方案:给Command传递当前项作为参数
无需依赖ListBox的SelectedItem,直接将Button绑定的LineButtons实例作为Command参数传递,即可直接获取当前点击的项,逻辑更简洁且无耦合。
修改后的XAML
给Button添加CommandParameter绑定,传递当前数据上下文:
<Button Style="{StaticResource ChangeButton}" Command="{Binding DataContext.ToggleDirectionCommand, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType= ListBox}}" CommandParameter="{Binding}"> <Button.Content> <TextBlock Text="{Binding BtnText}" TextDecorations="Underline" /> </Button.Content> </Button>
修改后的ViewModel代码
- 使用支持泛型参数的
RelayCommand(若自定义的RelayCommand未支持,需调整实现) - 修改命令执行方法,直接接收点击的项:
public DigitalIOViewModel() { btnlist = new ObservableCollection<LineButtons>(); CreateButtonList(); // 初始化带泛型参数的命令 ToggleDirectionCommand = new RelayCommand<LineButtons>(ToggleDirectionAction); } private void ToggleDirectionAction(LineButtons clickedItem) { if (clickedItem == null) return; // 直接修改当前项的文本,无需替换整个对象(LineButtons已实现属性通知) clickedItem.BtnText = clickedItem.BtnText == "1" ? "0" : "1"; // 如需索引,直接从集合中获取 int lineIndex = ButtonList.IndexOf(clickedItem); // 此处执行对应索引的业务逻辑 }
额外优化说明
- 原代码中替换整个
LineButtons对象属于冗余操作,直接修改BtnText属性即可触发UI更新,因为LineButtons已继承BindableBase实现属性变更通知。 - 无需依赖ListBox的选择状态,避免了点击操作与选择状态的耦合,逻辑更清晰。
关于方案二的疑问:Button的Command与ListBox的IsSelected可以同时使用,但在此场景下完全没必要,直接传递Command参数的方式更简洁高效。
内容的提问来源于stack exchange,提问作者Carsu
相关产品推荐
相关产品推荐

