You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

WPF中如何为ListBox内的每个Button触发事件并获取索引?

问题: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代码

  1. 使用支持泛型参数的RelayCommand(若自定义的RelayCommand未支持,需调整实现)
  2. 修改命令执行方法,直接接收点击的项:
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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.17 10:20:43