【问题标题】:Firing a RelayCommand from a button inside a WPF DataGrid从 WPF DataGrid 中的按钮触发 RelayCommand
【发布时间】:2012-08-24 04:01:23
【问题描述】:

我有一个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 中的数据上下文是错误的,但我不知道如何纠正它,也不知道如何传递按钮按下的特定行。任何和所有帮助我连接嵌入式按钮的建议都非常感谢!

【问题讨论】:

  • 嘿,我知道我在这里有点挖坟,但是您找到解决这种情况的方法了吗?我正在尝试完成同样的事情。

标签: wpf mvvm command


【解决方案1】:

这对你有帮助

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

【讨论】:

    【解决方案2】:

    This 问题/答案与您要查找的内容相似。

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

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

    【讨论】:

    • 它是相似的,但它与背后的常规代码有关,而不是我尝试使用的 MVVM 模式。不过谢谢!
    • 请仔细阅读答案。另外,在我的回答中,我给了你 MVVM 方式。您的绑定保持不变,但现在您可以在命令中使用一个参数。
    • 我明白了;谢谢。但是,根据我的问题标题,我正在寻找有关如何将其与中继命令一起使用的指导。简而言之,我希望得到一个更详细的答案。
    【解决方案3】:

    DataGridTemplate 内部的 Resharper/Intellisense 推断不正确。

    这可能会导致混淆,为了纠正这种情况,DataTemplate 应该有一个明确的 DataType 引用该行绑定到的类型。

    我发现有 2 种方法可以解决您的问题,使用相对绑定命令,以便它使用 DataGrid 的上下文进行绑定,而不是当前行,或者在具有的元素上创建 x:Name 属性您希望绑定到的上下文。

    我相信,如果您尝试访问祖先,则相对绑定会更清晰,但如果您需要绑定到树中其他地方的某些东西,那么 ElementName 可能会允许这样做。

    XAML:

    <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>
    

    类:

    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());
            }
        }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-06-07
      • 2011-06-06
      • 1970-01-01
      • 2013-11-17
      • 1970-01-01
      相关资源
      最近更新 更多