根据网格选择切换按钮 "CanExecute"

Toggle "CanExecute" of a button based on grid selection

我非常喜欢现代 UI 编程,现在我陷入了一个小型 C# WPF 应用程序,它基本上是一个 MVVM 设计模式的学习项目。

我有一个 DataGrid 和一些按钮来处理数据操作(添加、编辑、删除)。

我想要实现的:当没有选择网格中的行时,不应启用编辑按钮。

编辑按钮:

<Button Width="126" Height="22" Content="Edit" Margin="5,5,5,5" Command="{Binding KontoEdit}" />

网格:

   <DataGrid ItemsSource="{Binding Konten}"  SelectedItem="{Binding SelectedKonto}">
        <DataGrid.Columns>
            <DataGridTextColumn Binding="{Binding Path=KtoNr}" Header="Nr" IsReadOnly="True" />
            <DataGridTextColumn Binding="{Binding Path=Name}" Header="Name" IsReadOnly="True"  />
            <DataGridTextColumn Binding="{Binding Path=KtoArt}" Header="Kontoart" IsReadOnly="True"  />
            <DataGridTextColumn Binding="{Binding Path=KtoKlasse}" Header="Kontenklasse" IsReadOnly="True"  />
        </DataGrid.Columns>
    </DataGrid>

查看模型:

public class MainViewModel : INotifyPropertyChanged
{
    KontenDB ctx = new KontenDB();

    public MainViewModel()
    {
        FillKonten();

        CanKontoEditExecute = true ;

        KontoEdit = new RelayCommand(o => { DoKontoEdit(SelectedKonto); }, param => CanKontoEditExecute);
    }


    #region //Commands
    public void DoKontoEdit(Konten k)
    {
        //Edit the Selected Item
    }

    private ICommand _kontoEdit;
    public ICommand KontoEdit
    {
        get
        {
            return _kontoEdit;
        }
        set
        {
            _kontoEdit = value;
        }
    }

    private bool _canKontoEditExecute;

    public bool CanKontoEditExecute
    {
        get
        {
            return _canKontoEditExecute;
        }
        set
        {
            _canKontoEditExecute = value;
        }
    }

    #endregion //Commands
    private void FillKonten()
    {
        var q = (from k in ctx.Konten
                 select k).ToList();
        Konten = new ObservableCollection<Konten>(q);
    }


    private ObservableCollection<Konten> _konten;
    public ObservableCollection<Konten> Konten
    {
        get
        {
            return _konten;
        }
        set
        {
            _konten = value;
            NotifyPropertyChanged();
        }
    }

    private Konten _selectedKonto;
    public Konten SelectedKonto
    {
        get
        {
            return _selectedKonto;
        }
        set
        {
            _selectedKonto = value;
            NotifyPropertyChanged();
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;
    private void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}

模型由 EF6 生成。

编辑:RelayCommand class:

public class RelayCommand : ICommand
    {
        private Action<object> execute;

        private Predicate<object> canExecute;

        private event EventHandler CanExecuteChangedInternal;

        public RelayCommand(Action<object> execute)
            : this(execute, DefaultCanExecute)
        {
        }

        public RelayCommand(Action<object> execute, Predicate<object> canExecute)
        {
            if (execute == null)
            {
                throw new ArgumentNullException("execute");
            }

            if (canExecute == null)
            {
                throw new ArgumentNullException("canExecute");
            }

            this.execute = execute;
            this.canExecute = canExecute;
        }

        public event EventHandler CanExecuteChanged
        {
            add
            {
                CommandManager.RequerySuggested += value;
                CanExecuteChangedInternal += value;
            }

            remove
            {
                CommandManager.RequerySuggested -= value;
                CanExecuteChangedInternal -= value;
            }
        }

        public bool CanExecute(object parameter)
        {
            return canExecute != null && canExecute(parameter);
        }

        public void Execute(object parameter)
        {
            execute(parameter);
        }

        public void OnCanExecuteChanged()
        {
            EventHandler handler = CanExecuteChangedInternal;
            if (handler != null)
            {
                handler.Invoke(this, EventArgs.Empty);
            }
        }

        public void Destroy()
        {
            canExecute = _ => false;
            execute = _ => { return; };
        }

        private static bool DefaultCanExecute(object parameter)
        {
            return true;
        }
    }

那么在以下情况下我该如何实现:未选择数据网格中的行或 SelectedKonto 为 null CanKontoEditExecute 属性 更改为 false?

非常感谢您的帮助!

进行以下更正

SelectedItem="{Binding SelectedKonto, Mode=TwoWay}"


KontoEdit = new RelayCommand(o => { DoKontoEdit(SelectedKonto); }, param => SelectedKonto != nulll);


private object _selectedItem;
public object SelectedItem
{
    get { return _selectedItem; }
    set
    {
        _selectedItem = value;
        PropertyChanged(this,new PropertyChangedEventArgs("SelectedItem"));
    }
}

您应该在您的 ViewModel 中定义一个 SelectedItem 属性 并将其绑定到您的 DataGrid。

CS :

    private object _selectedItem;
    public object SelectedItem
    {
        get { return _selectedItem; }
        set
        {
            _selectedItem = value;
            if(PropertyChanged != null)
                PropertyChanged(this,new PropertyChangedEventArgs("SelectedItem"));

            DoKontoEdit.OnCanExecuteChanged();
        }
    }

XAML :

    <DataGrid SelectedItem="{Binding SelectedItem}">
        ....

那么你的命令应该这样初始化:

   new RelayCommand(o => { DoKontoEdit(SelectedKonto); }, _ => SelectedItem != null);

这是我认为更好的 ICommand 实现。当我决定何时评估 canExecute 委托并删除评估的 CommandManager 的 RequireySuggested 时,我更喜欢它。你可能想尝试一下。

         public class RelayCommand<T> : IRelayCommand
{
    private Predicate<T> _canExecute;
    private Action<T> _execute;

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

    public void Execute(T parameter)
    {
        _execute(parameter);
    }

    private bool CanExecute(T parameter)
    {
        return _canExecute == null ? true : _canExecute(parameter);
    }

    public virtual bool CanExecute(object parameter)
    {
        return parameter == null ? false : CanExecute((T)parameter);
    }

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

    public event EventHandler CanExecuteChanged;

    //public event EventHandler CanExecuteChanged
    //{
    //    add { CommandManager.RequerySuggested += value; }
    //    remove { CommandManager.RequerySuggested -= value; }
    //}


    public void RaiseCanExecuteChanged()
    {
        var temp = Volatile.Read(ref CanExecuteChanged);

        if (temp != null)
            temp(this, new EventArgs());
    }
}

public class RelayCommand : IRelayCommand
{
    private Func<bool> _canExecute;
    private Action _execute;

    public RelayCommand(Action execute , Func<bool> canExecute = null)
    {
        _execute = execute;
        _canExecute = canExecute;
    }

    public bool CanExecute(object parameter)
    {
        return _canExecute == null ? true : _canExecute();
    }

    //public event EventHandler CanExecuteChanged
    //{
    //    add { CommandManager.RequerySuggested += value; }
    //    remove { CommandManager.RequerySuggested -= value; }
    //}

    public event EventHandler CanExecuteChanged;

    public void RaiseCanExecuteChanged()
    {
        var temp = Volatile.Read(ref CanExecuteChanged);

        if (temp != null)
            temp(this, new EventArgs());
    }

    public void Execute(object parameter)
    {
        _execute();
    }
}