如何使用WPF为DataGrid上的每条记录添加删除按钮?

时间:2018-02-18 19:01:10

标签: c# wpf mvvm data-binding

我正在尝试在DataGrid中显示记录,并且对于网格上的每一行,我想显示一个删除该记录的按钮。

我已成功将按钮添加到dataGrid。但是,当加载视图时,"删除"为每条记录调用命令。换句话说,我确认对话框出现10次,因为我有10条记录要显示。

问题 如何阻止命令每次执行,只允许它在按钮点击时运行? 另外,如何将最后2列一直移动到最右边,使它们垂直对齐?

在我的ViewModel中,我添加了以下命令

public ICommand DeleteVendor
{
    get
    {
        MessageBoxResult confirmation = MessageBox.Show("Are you sure?", "Delete Confirmation", MessageBoxButton.YesNo);

        bool processDelete = (confirmation == MessageBoxResult.Yes);

        return new ActionCommand(p => HandleDeleteVendor(), p => processDelete);
    }
}

private void HandleDeleteVendor()
{
    if (SelectedVendor == null)
    {
        throw new Exception("No vendor was selected");
    }

    UnitOfWork.Vendors.Remove(SelectedVendor);
    UnitOfWork.Save();
}

然后在我看来,我添加了以下XAML代码

<DataGrid ItemsSource="{Binding Vendors}"
          SelectedItem="{Binding SelectedVendor}"
          AutoGenerateColumns="False"
          HorizontalAlignment="Stretch"
          VerticalAlignment="Center"
          CanUserAddRows="False">

    <DataGrid.Columns>

    <DataGridTextColumn Header="Name"
                            Binding="{Binding Name}" />
        <DataGridTextColumn Header="Account Number"
                            Binding="{Binding AccountCode}" />

        <DataGridTemplateColumn>
            <DataGridTemplateColumn.CellTemplate>
                <DataTemplate>
                    <Button VerticalAlignment="Center"
                            Command="{Binding RelativeSource={RelativeSource AncestorType={x:Type DataGrid}},
                            Path=DataContext.ShowVendor}">
                        <StackPanel Orientation="Horizontal">

                            <fa:FontAwesome Icon="Eye"
                                            FontSize="18" />
                            <Label Content="Details" 
                                   Padding="7 0 0 0" />
                        </StackPanel>
                    </Button>
                </DataTemplate>
            </DataGridTemplateColumn.CellTemplate>
        </DataGridTemplateColumn>

        <DataGridTemplateColumn>
            <DataGridTemplateColumn.CellTemplate>
                <DataTemplate>
                    <Button VerticalAlignment="Center"
                            Command="{Binding RelativeSource={RelativeSource AncestorType={x:Type DataGrid}},
                                  Path=DataContext.DeleteVendor}">
                        <StackPanel Orientation="Horizontal">

                            <fa:FontAwesome Icon="Trash"
                                            FontSize="18" />
                            <Label Content="Delete"
                                   Padding="7 0 0 0" />
                        </StackPanel>
                    </Button>
                </DataTemplate>
            </DataGridTemplateColumn.CellTemplate>
        </DataGridTemplateColumn>

    </DataGrid.Columns>

</DataGrid> 

如果需要,我的ICommand实施

public sealed class ActionCommand : ICommand
{
    private readonly Action<Object> Action;
    private readonly Predicate<Object> Allowed;

    /// <summary>
    /// Initializes a new instance of the <see cref="ActionCommand"/> class.
    /// </summary>
    /// <param name="action">The <see cref="Action"/> delegate to wrap.</param>
    public ActionCommand(Action<Object> action)
        : this(action, null)
    {
    }

    /// <summary>
    /// Initializes a new instance of the <see cref="ActionCommand"/> class.
    /// </summary>
    /// <param name="action">The <see cref="Action"/> delegate to wrap.</param>
    /// <param name="predicate">The <see cref="Predicate{Object}"/> that determines whether the action delegate may be invoked.</param>
    public ActionCommand(Action<Object> action, Predicate<Object> allowed)
    {
        if (action == null)
        {
            throw new ArgumentNullException("action", "You must specify an Action<T>.");
        }

        Action = action;
        Allowed = allowed;
    }

    /// <summary>
    /// Defines the method that determines whether the command can execute in its current state.
    /// </summary>
    /// <returns>
    /// true if this command can be executed; otherwise, false.
    /// </returns>
    /// <param name="parameter">Data used by the command.  If the command does not require data to be passed, this object can be set to null.</param>
    public bool CanExecute(object parameter)
    {
        if (Allowed == null)
        {
            return true;
        }

        return Allowed(parameter);
    }

    /// <summary>
    /// Defines the method to be called when the command is invoked.
    /// </summary>
    /// <param name="parameter">Data used by the command.  If the command does not require data to be passed, this object can be set to null.</param>
    public void Execute(object parameter)
    {
        Action(parameter);
    }

    /// <summary>
    /// Executes the action delegate without any parameters.
    /// </summary>
    public void Execute()
    {
        Execute(null);
    }

    /// <summary>
    /// Occurs when changes occur that affect whether the command should execute.
    /// </summary>
    public event EventHandler CanExecuteChanged
    {
        add
        {
            if (Allowed != null)
            {
                CommandManager.RequerySuggested += value;
            }
        }
        remove
        {
            if (Allowed != null)
            {
                CommandManager.RequerySuggested -= value;
            }
        }
    }

    /// <summary>
    /// Raises the <see cref="CanExecuteChanged" /> event.
    /// </summary>
    public void RaiseCanExecuteChanged()
    {
        CommandManager.InvalidateRequerySuggested();
    }

}

1 个答案:

答案 0 :(得分:1)

确认应该是delete方法的一部分,而不是命令定义的一部分:

public ICommand DeleteVendor
{
    get
    {
        return new ActionCommand(p => HandleDeleteVendor(), p => SelectedVendor != null);
    }
}

private void HandleDeleteVendor()
{
    MessageBoxResult confirmation = MessageBox.Show("Are you sure?", "Delete Confirmation", MessageBoxButton.YesNo);

    if (confirmation != MessageBoxResult.Yes);
       return;

    if (SelectedVendor == null)
    {
        // IMO, it is better to handle this situation gracefully and show messageBox with a warning
        throw new Exception("No vendor was selected");
    }

    UnitOfWork.Vendors.Remove(SelectedVendor);
    UnitOfWork.Save();
}