简体   繁体   中英

WPF MVVM RelayCommand multiple buttons on each DataGrid row, set enabled/disabled button state for selected row only

WPF

MVVM

RelayCommands

DataGrid with multiple buttons on each row.

Goal: To enable and disable some of the buttons for the selected row only, when I click on my "Start" button

Current Behavior: When I click the Start button, the buttons get enabled and disabled the same for all rows

In this image you can see that I clicked on the Start button and the command fired and correctly set the enabled/disabled state of the other buttons however it applied it to all rows.

I've been stuck trying to get it to apply for the selected item only. I'm currently trying to pass a parameter to the RelayCommand, if anyone could show me how to do that maybe it would work.

在此输入图像描述

XAML

<DataGrid x:Name="dataGridThreadView" Grid.Row="1" Grid.Column="0" AutoGenerateColumns="False" 
                  IsReadOnly="True" CanUserResizeRows="False" CanUserReorderColumns="True" Margin="4"  
                  CanUserAddRows="False" CanUserDeleteRows="False" CanUserSortColumns="True" 
                  EnableRowVirtualization="True" ItemsSource="{Binding Threads}"
                  SelectedItem="{Binding Path=SelectedThread, Mode=TwoWay}">

            <DataGrid.Columns>
                <DataGridTextColumn Header="Name"
                        Binding="{Binding Thread.Name}"/>
                <DataGridTemplateColumn Header="Actions">
                    <DataGridTemplateColumn.CellTemplate>
                        <DataTemplate>
                            <StackPanel Orientation="Horizontal" Margin="2">
                                <Button Name="buttonStartThread" Content="Start" Command="{Binding ElementName=dataGridThreadView,  Path=DataContext.StartCommand}" CommandParameter="{Binding SelectedThread}"/>
                                <Button Name="buttonSuspendThread" Content="Suspend" Command="{Binding ElementName=dataGridThreadView,  Path=DataContext.SuspendCommand}"/>
                                <Button Name="buttonResumeThread" Content="Resume" Command="{Binding ElementName=dataGridThreadView,  Path=DataContext.ResumeCommand}"/>
                                <Button Name="buttonInterruptThread" Content="Interrupt" Command="{Binding ElementName=dataGridThreadView,  Path=DataContext.InterruptCommand}"/>
                                <Button Name="buttonAbortThread" Content="Abort" Command="{Binding ElementName=dataGridThreadView,  Path=DataContext.AbortCommand}"/>
                            </StackPanel>
                        </DataTemplate>
                    </DataGridTemplateColumn.CellTemplate>
                </DataGridTemplateColumn>
            </DataGrid.Columns>
        </DataGrid>

ViewModel

 public class ThreadViewModel : PropertyChangedBase
{
    public ThreadModel SelectedThread { get; set; }
    public ObservableCollection<ThreadModel> Threads { get; set; }
    private bool _disableStartButton = false;
    private bool _disableSuspendButton = false;
    private bool _disableResumeButton = false;
    private bool _disableInterruptButton = false;
    private bool _disableAbortButton = false;

    public RelayCommand StartCommand { get; private set; } // should only be set once in vm during construction

    public ThreadViewModel()
    {
        _disableStartButton = false;
        _disableSuspendButton = true;
        _disableResumeButton = true;
        _disableInterruptButton = true;
        _disableAbortButton = true;
        Threads = new ObservableCollection<ThreadModel>();

        StartCommand = new RelayCommand(OnStart, CanStart);
    }

    private void OnStart(object template)
    {
        this._disableStartButton = true;
        StartCommand.RaiseCanExecuteChanged();
        _disableSuspendButton = false;
        SuspendCommand.RaiseCanExecuteChanged();
        _disableInterruptButton = false;
        InterruptCommand.RaiseCanExecuteChanged();
        _disableAbortButton = false;
        AbortCommand.RaiseCanExecuteChanged();
    }

    private bool CanStart()
    {
        return !this._disableStartButton;
    }
}

RelayCommand class

using System;
using System.Windows.Input;

namespace Multthreading
{
public class RelayCommand : ICommand
{
    Action _TargetExecuteMethod;
    Func<bool> _TargetCanExecuteMethod;

    public RelayCommand(Action executeMethod)
    {
        _TargetExecuteMethod = executeMethod;
    }

    public RelayCommand(Action executeMethod, Func<bool> canExecuteMethod)
    {
        _TargetExecuteMethod = executeMethod;
        _TargetCanExecuteMethod = canExecuteMethod;
    }

    public void RaiseCanExecuteChanged()
    {
        CanExecuteChanged(this, EventArgs.Empty);
    }
    #region ICommand Members

    bool ICommand.CanExecute(object parameter)
    {
        if (_TargetCanExecuteMethod != null)
        {
            return _TargetCanExecuteMethod();
        }
        if (_TargetExecuteMethod != null)
        {
            return true;
        }
        return false;
    }

    // Beware - should use weak references if command instance lifetime is longer than lifetime of UI objects that get hooked up to command
    // Prism commands solve this in their implementation
    public event EventHandler CanExecuteChanged = delegate { };

    void ICommand.Execute(object parameter)
    {
        if (_TargetExecuteMethod != null)
        {
            _TargetExecuteMethod();
        }
    }
    #endregion
}

public class RelayCommand<T> : ICommand
{
    Action<T> _TargetExecuteMethod;
    Func<T, bool> _TargetCanExecuteMethod;

    public RelayCommand(Action<T> executeMethod)
    {
        _TargetExecuteMethod = executeMethod;
    }

    public RelayCommand(Action<T> executeMethod, Func<T,bool> canExecuteMethod)
    {
        _TargetExecuteMethod = executeMethod;
        _TargetCanExecuteMethod = canExecuteMethod;
    }

    public void RaiseCanExecuteChanged() 
    {
         CanExecuteChanged(this, EventArgs.Empty); 
    }
    #region ICommand Members

    bool ICommand.CanExecute(object parameter)
    {
        if (_TargetCanExecuteMethod != null)
        {
            T tparm = (T)parameter;
            return _TargetCanExecuteMethod(tparm);
        }
        if (_TargetExecuteMethod != null)
        {
            return true;
        }
        return false;
    }

    // Beware - should use weak references if command instance lifetime is longer than lifetime of UI objects that get hooked up to command
    // Prism commands solve this in their implementation
    public event EventHandler CanExecuteChanged = delegate { };

    void ICommand.Execute(object parameter)
    {
        if (_TargetExecuteMethod != null)
        {
            _TargetExecuteMethod((T)parameter);
        }
    }
    #endregion
    }
}

A possible solution is the use of Bindings . Each row is bound to a different item, so each item can have their individual properties bound to those of the buttons.

You'll have to provide extra properties in the object's model. You can bind simple booleans to the IsEnabled property of the buttons. Put the default value on false . This way you can just edit the properties of your selected item and this should automatically change that of the corresponding buttons.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM