通过 ICommands 删除按钮单击时的 ListBoxItem

Posted

技术标签:

【中文标题】通过 ICommands 删除按钮单击时的 ListBoxItem【英文标题】:Remove ListBoxItem on button click through ICommands 【发布时间】:2019-09-11 18:06:00 【问题描述】:

我刚开始使用 XAML/WPF,我的脑海中出现了很多问题。其中之一是我们如何通过 ICommand 接口绑定按钮单击以删除ListBoxItem。我创建了一个简单的 WPF 项目,这是我的 XAML:

<ListBox Name="lb" HorizontalAlignment="Left" Height="129" Margin="15,17,0,0" VerticalAlignment="Top" Width="314" Grid.ColumnSpan="2" >
    <ListBox.Resources>
        <Style TargetType="ListBoxItem">
            <Setter Property="Height" Value="30" />
            <Setter Property="OverridesDefaultStyle" Value="true" />
            <Setter Property="SnapsToDevicePixels" Value="true" />
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="ListBoxItem">
                        <StackPanel Orientation="Horizontal">
                            <CheckBox Margin="5,5" Height="18" IsChecked="TemplateBinding IsSelected">
                                <ContentPresenter Content="TemplateBinding Content"/>
                            </CheckBox>
                            <Button Content="[x]" Height="22" Width="22" HorizontalAlignment="Right" 
                                    Command="Binding ElementName=lb, Path=DataContext.DeleteItemCommand, RelativeSource=RelativeSource FindAncestor, AncestorType=x:Type ListBox" CommandParameter="Binding "/>
                        </StackPanel>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>
    </ListBox.Resources>
    <ListBoxItem Content="Foo" />
    <ListBoxItem Content="Bar" />
</ListBox>

这是我的窗口:

public partial class MainWindow : Window

    public MainWindow()
    
        InitializeComponent();
        DataContext = new Context(); // Also tried before InitializeComponent()
    

    public class Context
    
        public ICommand DeleteItemCommand = new DeleteItemCommand();
    

DeleteItemCommand 在哪里:

public class DeleteItemCommand : ICommand

    public event EventHandler CanExecuteChanged;

    public bool CanExecute(object parameter)
    
        return true;
    

    public void Execute(object parameter)
    
        MessageBox.Show("Meep");
    

问题是:

    为什么不显示消息框?如何让它发挥作用? 如何检索触发按钮的索引/ListBoxItem 点击? 如何将按钮与行尾对齐?

非常感谢!

【问题讨论】:

【参考方案1】:

您遇到的一个问题是您的 ICommand 只是一个变量。

您需要一个公共属性才能绑定。

更像

    public ICommand DeleteItemCommand get;set; = new DeleteItemCommand();

另一个问题是您的元素名称。这取决于名称范围,我想您会发现列表框位于另一个名称范围内。

相反,只需将相对源绑定与祖先类型 ListBox 一起使用。

大概。

Command="Binding DataContext.DeleteItemCommand,
RelativeSource=RelativeSource AncestorType=x:Type ListBox

顺便说一句。

我建议研究一个框架以使命令等更容易。

MVVMLight 是我的建议。使用 nuget mvvmlightlibs 添加到项目中。 https://msdn.microsoft.com/en-gb/magazine/dn237302.aspx?f=255&MSPPError=-2147217396

以下内容基于我已经拥有的一些代码,所以它是说明性的,而不是你在做什么。

查看:

<Window.DataContext>
    <local:MainWindowViewModel/>
</Window.DataContext>
<Grid>
    <ListBox ItemsSource="Binding People"
             HorizontalContentAlignment="Stretch">
        <ListBox.ItemTemplate>
            <DataTemplate>
                <Grid>
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition Width="*"/>
                        <ColumnDefinition Width="100"/>
                    </Grid.ColumnDefinitions>
                 <TextBlock Text="Binding LastName"/>
                    <Button Content="Delete"
                            Command="Binding DataContext.DeletePersonCommand, RelativeSource=RelativeSource AncestorType=ListBox"
                            CommandParameter="Binding"
                            Grid.Column="1"/>
                </Grid>

            </DataTemplate>
        </ListBox.ItemTemplate>
    </ListBox>
</Grid>

Viewmodel 使用 mvvmlight 中的 relaycommand

using GalaSoft.MvvmLight.CommandWpf;
using System.Collections.ObjectModel;
namespace wpf_99

public class MainWindowViewModel : BaseViewModel

    private RelayCommand<Person> deletePersonCommand;
    public RelayCommand<Person> DeletePersonCommand
    
        get
        
            return deletePersonCommand
            ?? (deletePersonCommand = new RelayCommand<Person>(
              (person) =>
              
                  People.Remove(person);
              
             ));
        
    

    private ObservableCollection<Person> people = new ObservableCollection<Person>();

    public ObservableCollection<Person> People
    
        get  return people; 
        set  people = value; 
    

    public MainWindowViewModel()
    
        People.Add(new Person  FirstName = "Chesney", LastName = "Brown" );
        People.Add(new Person  FirstName = "Gary", LastName = "Windass" );
        People.Add(new Person  FirstName = "Liz", LastName = "McDonald" );
        People.Add(new Person  FirstName = "Carla", LastName = "Connor" );
    


BaseViewModel 与 inotifypropertychanged 上的 msdn 文章非常相似:

public  class BaseViewModel : INotifyPropertyChanged

    public event PropertyChangedEventHandler PropertyChanged;
    protected void RaisePropertyChanged([CallerMemberName] String propertyName = "")
    
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    

Mvvmlight 有自己的基本视图模型,但您不能序列化继承自它的 vm。

人: 公共类人:BaseViewModel 私有字符串名字;

    public string FirstName
    
        get  return firstName; 
        set  firstName = value; RaisePropertyChanged(); 
    
    private string lastName;

    public string LastName
    
        get  return lastName; 
        set  lastName = value; RaisePropertyChanged(); 
    

【讨论】:

嗨@Andy!谢谢回复。为命令提供访问器使其工作。这个丑陋的RelativeSource 有什么解决方法吗?!另外,如何将按钮与绝对右侧对齐?还有....如何检索按钮的 ListBoxItem 索引? 你意识到每个线程应该只是一个问题?相对源 - 您可以将数据上下文作为资源,但我建议您不要认为它看起来很丑陋。至少现在。如果你想在右边和左边一些东西,请使用带有列的网格而不是 stackpanel。不要使用索引,将行视图模型传递给命令 CommandParameter="Binding" 我对 WPF 比较陌生,最好有一个完整的示例。在这种情况下,围绕同一上下文创建多个线程没有任何意义。另外,我不知道如何“将行视图模型传递给命令”。

以上是关于通过 ICommands 删除按钮单击时的 ListBoxItem的主要内容,如果未能解决你的问题,请参考以下文章

删除按钮单击时的地图注释(创建注释)

我将如何在按钮单击时从 uiview 中删除 UIBezierPath。?

通过打字稿更改按钮单击时的svg颜色

通过单击按钮循环浏览 Jlabel 的图像时的 for 循环问题

在 iOS 中单击按钮时的标签和框架扩展

如何通过单击按钮并返回原始形式隐藏其右侧/左侧网格时的全宽网格?