单击未选中的 listviewitem 中的按钮并获取选中的项目

Clicking a button in a listviewitem that is not selected and getting selected item

我对如下例所示的列表视图设置有疑问。当我单击扩展器下方的按钮时 header 我也希望该项目也被选中,但我看到的是虽然按钮命令有效,但所选项目仍然是上一个所选项目,而不是我的按钮所在的项目。单击按钮时如何选择项目?

我试过像这样设置一个 ControlTemplate,但是没有用。

<Style TargetType="{x:Type ListViewItem}">
    <Setter Property="HorizontalContentAlignment" Value="Stretch" />
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type ListViewItem}">
                    <ContentPresenter HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" />
                    <ControlTemplate.Triggers>
                        <Trigger Property="IsKeyboardFocusWithin" Value="True">
                            <Setter Property="IsSelected" Value="True" />
                        </Trigger>
                    </ControlTemplate.Triggers>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>


<ListView ItemsSource="{Binding MyItemSource,
                                        Mode=TwoWay}"
                  SelectedItem="{Binding MySelectedItem,
                                         Mode=TwoWay}">
    <ListView.ItemTemplate>
        <DataTemplate>
            <Expander IsExpanded="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type ListViewItem}}, Path=IsSelected}">
                <Expander.Header>
                    <Button Command={Binding MyCommand}>Click Me</Button>
                </Expander.Header>
                <!-- content here -->
            </Expander>
        </DataTemplate>
    </ListView.ItemTemplate>
</ListView>

我建议在主 ViewModel 中定义一个命令 SelectItem,它将要选择的项目作为参数。此命令的执行方法然后可以设置 MySelectedItem 属性,将项目 ViewModel 上的 属性 IsSelected 设置为 true 并调用所有进一步的操作项目本身(即现在由 MyCommand 执行的内容)。使用 ViewModel 中的选择逻辑和干净的绑定,您甚至根本不需要使用 ListView,但可以坚持使用普通的 ItemsControl:

然后 XAML 看起来像这样:

<ItemsControl ItemsSource="{Binding MyItemSource}">
    <ItemsControl.ItemTemplate>
        <DataTemplate>
            <Expander IsExpanded="{Binding IsSelected}">
                <Expander.Header>
                    <Button Command="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=ItemsControl}, Path=DataContext.SelectItem}"
                            CommandParameter="{Binding"}>Click Me</Button>
                </Expander.Header>
                <!-- content here -->
            </Expander>
        </DataTemplate>
    </ItemsControl.ItemTemplate>
</ItemsControl>

MainViewModel 看起来像这样:

public class MainViewModel : INotifyPropertyChanged
{
    public ObservableCollection<ItemViewModel> MyItemsSource { get; private set; }

    public ItemViewModel SelectedItem { get... set... }

    public ICommand SelectItem { get; private set; }

    ctor()...

    private void ExecuteSelectItem(ItemViewModel item)
    {
        SelectedItem = item;
        foreach (var i in MyItemsSource) i.IsSelected = false;
        item.IsSelected = true;
        item.DoSomething();
    }
}

我一直发现使用 ItemsControl 自己实现几行选择逻辑更容易,而不是处理 ListView 选择的混乱绑定。在我看来,实现自定义选择行为(多个项目,只允许某些组合等)是非常直观的。您可以使用 IsSelected 属性 轻松地为所选项目应用自定义样式。

您可以在您的视图模型中尝试类似的操作,在 setter 中添加 if 语句:

private object _mySelectedItem;
    public object MySelectedItem
    {
        get { return _mySelectedItem; }
        set 
        {
            if (_mySelectedItem != value && value != null)
            {
                _mySelectedItem = value;
                OnPropertyChanged("MySelectedItem");
            }
        }
    }