从WPF DataGrid内的按钮触发RelayCommand

时间:2012-08-29 13:59:59

标签: wpf mvvm command

我有一个DataGrid显示XAML中定义的客户列表,如下所示绑定到我的ViewModel:

<DataGrid Name="CustomersDataGrid" ItemsSource="{Binding Customers}">
    <DataGrid.Columns>
        <DataGridTemplateColumn>
            <DataGridTemplateColumn.CellTemplate>
                <DataTemplate>
                    <Button Command="{Binding showCustomerCommand}" 
                        Content="{Binding Path=Name}"/>
                </DataTemplate>
            </DataGridTemplateColumn.CellTemplate>
        </DataGridTemplateColumn>
    </DataGrid.Columns>
</DataGrid>

网格的显示效果很好。我希望能够显示单个客户的详细信息。以前,我为所选行设置了一个绑定,并在页面上有一个按钮绑定到以下命令:

RelayCommand _showCustomerCommand;
public ICommand showCustomerCommand
{
    get
    {
        if (_showCustomerCommand == null)
        {
            _showCustomerCommand = new RelayCommand(param => this.ShowCustomer());
        }
        return _showCustomerCommand;
    }
}

private void ShowCustomer()
{
    if (Parent != null)
    {
        // Handle Customer Display Here
    }
}

这很好用。但我希望能够单击单个行内的按钮,而不是基于所选行的单个按钮。我知道上面的XAML中的datacontext是错误的,但我不知道如何纠正它,也不知道如何传递按钮按下的特定行。感谢收到任何和所有建议,以帮助我连接我的嵌入式按钮!

3 个答案:

答案 0 :(得分:29)

这会对你有所帮助

<DataGridTemplateColumn.CellTemplate>
                    <DataTemplate>
                        <StackPanel>
                            <Button 
                          Command="{Binding Path=DataContext.showCustomerCommand,       
 RelativeSource= {RelativeSource FindAncestor,
  AncestorType={x:Type DataGrid}}}">
                            </Button>
                        </StackPanel>
                    </DataTemplate>
                </DataGridTemplateColumn.CellTemplate>

答案 1 :(得分:6)

This问题/答案与您正在寻找的内容类似。

您可以将行的ID绑定到命令参数

<Button Click="Button_Click" Command="{Binding showCustomerCommand}" 
    CommandParameter="{Binding Path=ID}">View Details</Button>

答案 2 :(得分:2)

<Window x:Class="TestBindings.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:local="clr-namespace:TestBindings" mc:Ignorable="d" Title="MainWindow" Height="350" Width="525"> <Window.DataContext> <local:WindowViewModel/> </Window.DataContext> <Grid ShowGridLines="True"> <Grid.ColumnDefinitions> <ColumnDefinition Width="1*"/> <ColumnDefinition Width="1*"/> </Grid.ColumnDefinitions> <GroupBox x:Name="Test" Header="Grid" Grid.Column="0"> <!-- #2 Add a name to the element you need to bind to. --> <DataGrid ItemsSource="{Binding RowList}" IsSynchronizedWithCurrentItem="True" SelectionUnit="FullRow" > <DataGrid.Columns> <DataGridTemplateColumn> <DataGridTemplateColumn.CellTemplate> <DataTemplate DataType="local:RowViewModel"><!-- Needed for correct inference in XAML editor --> <!-- #1 Cleaner solution as it doesn't depend on names --> <Button Content="{Binding Path=RowCount}" Command="{Binding Path=DataContext.NineCommand, RelativeSource={RelativeSource AncestorType={x:Type DataGrid}}}" CommandParameter="{Binding}" /> <!-- #2 Naming the element explicitly probably allows you to jump elsewhere in the tree --> <Button Content="{Binding Path=RowCount}" Command="{Binding Path=DataContext.NineCommand, ElementName=Test}" CommandParameter="{Binding}" /> </DataTemplate> </DataGridTemplateColumn.CellTemplate> </DataGridTemplateColumn> </DataGrid.Columns> </DataGrid> </GroupBox> <GroupBox Header="{Binding Path=SelectedRow.RowCount}" Grid.Column="1"> <Label Content="{Binding Path=RowList/RowCount}"></Label> </GroupBox> </Grid> </Window> 内的Resharper / Intellisense推理不正确。

这可能会引起混淆,为了纠正这个问题,DataTemplate应该有一个显式的DataType引用该行绑定的类型。

我找到了两种方法来解决你的问题,使用命令的相对绑定,以便它使用DataGrid的Context而不是当前行绑定,或者在一个元素上创建一个x:Name属性你希望绑定的上下文。

我相信如果你试图访问一个祖先,相对绑定会更清晰,但是如果你需要绑定到树中其他地方的东西,那么ElementName就可以允许它。

XAML:

using System;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Windows.Data;
using System.Windows.Input;
using Microsoft.Expression.Interactivity.Core;
using Microsoft.Practices.Prism.ViewModel;

namespace TestBindings
{
    public class RowViewModel : NotificationObject
    {
        private static int _max = 0;
        private int _rowCount;

        public int RowCount
        {
            get { return _rowCount; }
            set
            {
                if (_rowCount == value) return;
                _rowCount = value;
                RaisePropertyChanged(nameof(RowCount));
            }
        }

        public RowViewModel()
        {
            _rowCount = _max++;
        }
    }

    public class TypedCommand<T> : ICommand
    {
        private readonly Predicate<T> _canExecute;
        private readonly Action<T> _execute;

        public TypedCommand(Action<T> execute, Predicate<T> canExecute = null)
        {
            this._canExecute = canExecute;
            this._execute = execute;
        }

        public bool CanExecute(object parameter)
        {
            return _canExecute == null || _canExecute((T) parameter);
        }

        public void Execute(object parameter)
        {
            _execute?.Invoke((T) parameter);
        }

        public event EventHandler CanExecuteChanged;
    }

    public class WindowViewModel : NotificationObject
    {
        public ObservableCollection<RowViewModel> RowList { get; } = new ObservableCollection<RowViewModel>();

        private RowViewModel _selectedRow;

        public RowViewModel SelectedRow
        {
            get { return _selectedRow; }
            private set
            {
                if (_selectedRow == value) return;
                _selectedRow = value;
                RaisePropertyChanged(nameof(SelectedRow));
            }
        }

        private static readonly Action<RowViewModel> DeleteAction = new Action<RowViewModel>(row => row.RowCount=99);

        public ICommand NineCommand { get; } = new TypedCommand<RowViewModel>(DeleteAction);

        public WindowViewModel()
        {
            //0
            RowList.Add(new RowViewModel());
            //1
            RowList.Add(new RowViewModel());
            //2
            RowList.Add(new RowViewModel());
        }
    }
}

类别:

@FunctionalInterface