How to add a delete button for every record on a D

2019-08-08 17:50发布

问题:

I am trying to display record in a DataGrid and for each row on the grid, I want to show a button to delete that record.

I added the button successfully to the dataGrid. However, when the view is loaded, the "delete" command is called for every record. In another word, I the confirm dialog appears 10 time because I have 10 records to display.

Questions How to I prevent the command from being executed each time and only allow it to run on button click? Also, how can I move the last 2 column all the way to the far right so they are vertically aligned?

In my ViewModel, I added the following command

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

Then in my view I added the following XAML code

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

If needed here is my ICommand implementation

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:

confirmation should be a part of delete method, not a part of command definition:

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