Wpf 如何在ListView中捕获按钮按下

Wpf 如何在ListView中捕获按钮按下,wpf,listview,button,Wpf,Listview,Button,我在WPF列表视图中显示一个项目列表,项目有数量、订单代码和描述。这些列绑定到视图模型中保存的ObservableCollection中的字段。这一切都是非常标准的,正如预期的那样。然而,在ListView的Quantity列中,我添加了两个按钮+和-,其思想是当按下它们时,数量的值要么递增,要么递减。问题是,因为这些按钮没有绑定到ObservaleCollection中的字段,所以我无法从列表视图中按下的按钮获取到ObservaleCollection中记录的链接。我尝试在ListView中选

我在WPF列表视图中显示一个项目列表,项目有数量、订单代码和描述。这些列绑定到视图模型中保存的ObservableCollection中的字段。这一切都是非常标准的,正如预期的那样。然而,在ListView的Quantity列中,我添加了两个按钮+和-,其思想是当按下它们时,数量的值要么递增,要么递减。问题是,因为这些按钮没有绑定到ObservaleCollection中的字段,所以我无法从列表视图中按下的按钮获取到ObservaleCollection中记录的链接。我尝试在ListView中选择项目,但按下按钮时选择的是按钮,而不是ListView项目。按下按钮时,我还捕获了鼠标指针下的项目,但可以使用键盘按下

我觉得必须有一个(简单的!)方法来做到这一点,但我找不到它

这是XAML:

<ListViewName="AccessoriesContent" >
    <ListView.View>
        <GridView>
            <GridView.Columns>
                <GridViewColumn Header="Select">
                    <GridViewColumn.CellTemplate>
                        <DataTemplate>
                            <StackPanel  Orientation="Horizontal" Name="QuantityStack">
                                <Button Name="SubtractAccessoryButton" Command="vx:DataCommands.SubtractAccessory" Content="-" />
                                <TextBox Name="QuantityTextBox" Text="{Binding Quantity, Mode=TwoWay}" />
                                <Button Name="AddAccessoryButton" Command="vx:DataCommands.AddAccessory" Content="+" />
                            </StackPanel>
                        </DataTemplate>
                    </GridViewColumn.CellTemplate>
                </GridViewColumn>
                <GridViewColumn Header="Order Code"  DisplayMemberBinding="{Binding OrderCode}" />
                <GridViewColumn Header="Description"  DisplayMemberBinding="{Binding Description}" />
            </GridView.Columns>
        </GridView>
    </ListView.View>
</ListView>
以及ViewModel:

class AccessoryViewModel
{
    ObservableCollection<AccessoryData> _AccessoryCollection =
    new ObservableCollection<AccessoryData>();

    public ObservableCollection<AccessoryData> AccessoryCollection
    { get { return _AccessoryCollection; } }

    public void PopulateAccessories(string order_code)
    {
        // Read the data and populate AccessoryCollection
    }
}

public class AccessoryData : INotifyPropertyChanged
{
    private int _quantity;
    public int Quantity
    {
        get { return _quantity; }
        set
        {
            this._quantity = value;
            Notify("Quantity");
        }
    }
    public string OrderCode { get; set; }
    public string Description { get; set; }


    public event PropertyChangedEventHandler PropertyChanged;
    protected void Notify(string propName)
    {
        if (this.PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(propName));
        }
    }
}
类访问视图模型
{
ObservableCollection\u AccessoryCollection=
新的可观察集合();
公共可观测集合附件集合
{get{return{U AccessoryCollection;}}
公共void PopulateAccessories(字符串顺序_代码)
{
//读取数据并填充AccessoryCollection
}
}
公共类访问数据:INotifyPropertyChanged
{
私人国际单位数量;
公共整数
{
获取{返回_数量;}
设置
{
这个。_数量=值;
通知(“数量”);
}
}
公共字符串顺序码{get;set;}
公共字符串说明{get;set;}
公共事件属性更改事件处理程序属性更改;
受保护的void Notify(字符串propName)
{
if(this.PropertyChanged!=null)
{
PropertyChanged(这是新PropertyChangedEventArgs(propName));
}
}
}

除此之外,我还有两种方法Subtract Accessory和AddAccessory,它们是由按钮触发的,但我还没有用任何有效的方法填充它们。

您可以通过按钮上唯一标识当前项的
命令参数
传递当前项。这样,在执行命令时,你就知道你在说什么了。如果你在物品中找不到唯一的标记,你甚至可以传递整个物品


您似乎没有发布您使用的命令

无论如何,如果您确实使用命令,您可以使用视图模型上存在的实例命令(然后需要将命令绑定到
DataContext
上的command属性),从而访问
数量
,或者您可以将视图模型作为
CommandParameter
传递,只需将其设置为
{Binding}
,然后在命令中可以将参数强制转换为VM并更改
数量


(如果要使用
单击
事件,您可以将
发送者
强制转换为
按钮
,并将其
数据上下文
强制转换为VM)

另一个选项是创建RelayCommand(请参阅)。在此模型中,您可以在每个项目上创建ICommand属性。然后将此属性设置为一个新的RelayCommand,该命令接受您希望在激活该命令时运行的委托。所以这可能是对AccessoryData的QuantityUp方法和QuantityDown方法。一旦设置好ICommand属性,您只需像这样绑定到它,其中QuantityUpCommand是您的ICommand属性

 <GridViewColumn Header="" >
   <GridViewColumn.CellTemplate>
     <DataTemplate>
       <Button Height="15" Width="15" Content="+" Command="{Binding QuantityUpCommand}"/>
     </DataTemplate>
   </GridViewColumn.CellTemplate>
 </GridViewColumn>
RelayCommand看起来像这样:

public class RelayCommand: ICommand
{
        #region Fields

        readonly Action<object> _execute;
        readonly Predicate<object> _canExecute;

        #endregion // Fields

        #region Constructors

        public RelayCommand(Action<object> execute)
            : this(execute, null)
        {
        }

        public RelayCommand(Action<object> execute, Predicate<object> canExecute)
        {
            if (execute == null)
                throw new ArgumentNullException("execute");

            _execute = execute;
            _canExecute = canExecute;
        }
        #endregion // Constructors

        #region ICommand Members

        public bool CanExecute(object parameter)
        {
            return _canExecute == null ? true : _canExecute(parameter);
        }

        public event EventHandler CanExecuteChanged
        {
            add { CommandManager.RequerySuggested += value; }
            remove { CommandManager.RequerySuggested -= value; }
        }

        public void Execute(object parameter)
        {
            _execute(parameter);
        }

        #endregion // ICommand Members
}
公共类RelayCommand:ICommand
{
#区域字段
只读操作_执行;
只读谓词_canExecute;
#endregion//字段
#区域构造函数
公共中继命令(操作执行)
:此(执行,空)
{
}
公共RelayCommand(操作执行,谓词canExecute)
{
if(execute==null)
抛出新的ArgumentNullException(“执行”);
_执行=执行;
_canExecute=canExecute;
}
#endregion//构造函数
#区域ICommand成员
公共布尔CanExecute(对象参数)
{
返回_canExecute==null?true:_canExecute(参数);
}
公共事件事件处理程序CanExecuteChanged
{
添加{CommandManager.RequerySuggested+=value;}
删除{CommandManager.RequerySuggested-=value;}
}
public void Execute(对象参数)
{
_执行(参数);
}
#endregion//ICommand成员
}

+1指出您可以从
按钮获取ViewModel。DataContext
H.B。感谢您的回答。我使用的是实例命令,但CommandParameter有问题,我将再看一眼。对于MVVM框架的新手,我想在这里指出,DataContext并不总是包含ViewModel。程序员必须在代码中显式地将DataContext设置为与ViewModel相等,以便以后能够从DataContext中获取ViewModel。@MarkRucker:在这种情况下不是这样的,在
ListView
中,项模板中的元素总是将VM作为DataContext。@Marcus:正如我所说,如果在实例上有命令,您甚至不需要命令参数,因为命令与
数量
位于同一对象上,因此您可以直接访问它。如果您使用某种形式的中继命令,那么值得注意的是,如果您使用lambdas来创建execute方法,您应该初始化constructor中的command backing字段,而不是字段初始化器中的command backing字段,它是准静态的,因此无法访问对象实例
private RelayCommand _quantityUpCommand;
public ICommand QuantityUpCommand
{
    get
    {
        if (_quantityUpCommand == null)
        {
            _quantityUpCommand = new RelayCommand(QuantityUp);
        }
        return _quantityUpCommand;
    }
}

public void QuantityUp(object obj)
{
   Quantity++;
}
public class RelayCommand: ICommand
{
        #region Fields

        readonly Action<object> _execute;
        readonly Predicate<object> _canExecute;

        #endregion // Fields

        #region Constructors

        public RelayCommand(Action<object> execute)
            : this(execute, null)
        {
        }

        public RelayCommand(Action<object> execute, Predicate<object> canExecute)
        {
            if (execute == null)
                throw new ArgumentNullException("execute");

            _execute = execute;
            _canExecute = canExecute;
        }
        #endregion // Constructors

        #region ICommand Members

        public bool CanExecute(object parameter)
        {
            return _canExecute == null ? true : _canExecute(parameter);
        }

        public event EventHandler CanExecuteChanged
        {
            add { CommandManager.RequerySuggested += value; }
            remove { CommandManager.RequerySuggested -= value; }
        }

        public void Execute(object parameter)
        {
            _execute(parameter);
        }

        #endregion // ICommand Members
}