【问题标题】:How to capture a button press in a ListView如何在 ListView 中捕获按钮按下
【发布时间】:2012-02-06 21:27:05
【问题描述】:

我在 WPF ListView 中显示项目列表,这些项目具有数量、订单代码和描述。这些列绑定到视图模型中保存的 ObservableCollection 中的字段。这都是非常标准的,并且可以按预期工作。但是,在 ListView 的 Quantity 列中,我添加了两个按钮 + 和 -,其想法是当按下它们时,数量的值会增加或减少。问题是因为这些按钮没有绑定到 ObservableCollection 中的字段,所以我无法从列表视图中按下的按钮获取到 ObservableCollection 中记录的链接。我尝试在 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>

背后的代码:

    public MainWindow()
    {
        //CommandBindings.Add(
        InitializeComponent();
        AccessoryVM = new AccessoryViewModel();
        AccessoriesContent.ItemsSource = AccessoryVM.AccessoryCollection;
    }

还有 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));
        }
    }
}

除此之外,我还有两个由按钮触发的方法 SubtractAccessory 和 AddAccessory,但我还没有用任何可行的方法填充它们。

【问题讨论】:

    标签: wpf listview button


    【解决方案1】:

    另一个选项是创建一个 RelayCommand(请参阅here)。在此模型中,您为每个项目创建一个 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>
    

    AccessoryData 看起来像这样

    private RelayCommand _quantityUpCommand;
    public ICommand QuantityUpCommand
    {
        get
        {
            if (_quantityUpCommand == null)
            {
                _quantityUpCommand = new RelayCommand(QuantityUp);
            }
            return _quantityUpCommand;
        }
    }
    
    public void QuantityUp(object obj)
    {
       Quantity++;
    }
    

    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
    }
    

    【讨论】:

    • 谢谢马克,这正是我要找的东西 - 很好用。
    • 很高兴我能帮上忙。在我遇到这个解决方案之前,我的头撞在了同一堵墙上一段时间。如果这对您有用,如果您将此标记为您接受的解决方案,我将不胜感激。
    【解决方案2】:

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

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

    (如果您要使用Click 事件,您可以将sender 转换为Button 并将其DataContext 转换为VM)

    【讨论】:

    • +1 指出您可以从 Button.DataContext 获取 ViewModel
    • H.B.感谢您的回答。我使用的是实例命令,但 CommandParameter 出现问题,我再看看。
    • 对于刚接触 MVVM 框架的程序员,我想在这里指出,DataContext 并不总是包含 ViewModel。程序员必须在代码中明确地将 DataContext 设置为等于 ViewModel,以便以后能够从 DataContext 中获取 ViewModel。
    • @MarkRucker:在这种情况下并非如此,在ListView 中,项目模板中的元素总是将 VM 作为 DataContext。
    • @Marcus:正如我所说,如果实例上有命令,您甚至不需要命令参数,因为该命令与Quantity 在同一个对象上,因此您可以直接访问它.如果您使用某种形式的中继命令,值得注意的是,如果您使用 lambdas 创建执行方法,您应该在构造函数中初始化命令支持字段,而不是在准静态的字段初始化程序中,因此无法访问到对象实例。
    【解决方案3】:

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

    &lt;Button Name="AddAccessoryButton" Command="vx:DataCommands.AddAccessory" CommandParameter="{Binding}" Content="+" /&gt;

    【讨论】:

      猜你喜欢
      • 2015-07-13
      • 1970-01-01
      • 1970-01-01
      • 2011-12-06
      • 1970-01-01
      • 1970-01-01
      • 2014-03-28
      • 2019-04-17
      • 2015-10-07
      相关资源
      最近更新 更多