1

我无法让我的命令的 CanExecute 方法工作属性。我已将命令绑定到 DataGrid 内的按钮。我已将 CommandParameter 绑定到按钮的 DataContext,它恰好是 DataGrid 中一行的记录。

我期望发生的是在 CommandParameter 绑定更改时重新评估 CanExecute 方法,在这种情况下,这将是设置行的 DataContext 属性。但是,不是针对行数据评估 CanExecute 方法,而是在行获取其 DataContext 之前评估 CanExecute 方法,并且在更新 DataContext 之后永远不会重新评估它。

你能告诉我如何让我的命令的 CanExecute 方法针对每一行的 DataContext 进行评估吗?

我创建了一个示例应用程序来演示我的问题。这是代码:

MainWindow.xaml 的代码隐藏

public partial class MainWindow : Window
{
    public ObservableCollection<LogRecord> Records { get; private set; }
    public ICommand SignOutCommand { get; private set; }
    public MainWindow()
    {
        InitializeComponent();
        DataContext = this;
        Records = new ObservableCollection<LogRecord>();
        SignOutCommand = new SignOutCommand();
        CreateDemoData();
    }
    private void CreateDemoData()
    {
        for (int i = 0; i < 5; i++)
        {
            Records.Add(new LogRecord());
        }
    }
}

public class LogRecord : INotifyPropertyChanged
{
    private DateTime _EntryTime;
    public DateTime EntryTime
    {
        get { return _EntryTime; }
        set
        {
            if (_EntryTime == value) return;
            _EntryTime = value;
            RaisePropertyChanged("EntryTime");
        }
    }

    private DateTime? _ExitTime;
    public DateTime? ExitTime
    {
        get { return _ExitTime; }
        set 
        {
            if (_ExitTime == value) return;
            _ExitTime = value;
            RaisePropertyChanged("ExitTime");
        }
    }

    public LogRecord()
    {
        EntryTime = DateTime.Now;
    }

    #region Implementation of INotifyPropertyChanged

    public event PropertyChangedEventHandler PropertyChanged;

    public void RaisePropertyChanged(string propertyName)
    {
        if (PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
    }

    #endregion
}

public class SignOutCommand : ICommand
{
    #region Implementation of ICommand

    public void Execute(object parameter)
    {
        var record = parameter as LogRecord;
        if (record == null) return;
        record.ExitTime = DateTime.Now;
    }

    public bool CanExecute(object parameter)
    {
        var record = parameter as LogRecord;
        return record != null && !record.ExitTime.HasValue;
    }

    public event EventHandler CanExecuteChanged;

    #endregion
}

MainWindow.xaml 的 XAML

<Window x:Class="Command_Spike.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="MainWindow"
    Width="525"
    Height="350">
<DataGrid ItemsSource="{Binding Path=Records}" IsReadOnly="True" AutoGenerateColumns="False">
    <DataGrid.Columns>
        <DataGridTextColumn Header="Entry Time" Binding="{Binding Path=EntryTime}" />
        <DataGridTextColumn Header="Exit Time" Binding="{Binding Path=ExitTime}" />
        <DataGridTemplateColumn>
            <DataGridTemplateColumn.CellTemplate>
                <DataTemplate>
                    <Button Command="{Binding RelativeSource={RelativeSource Mode=FindAncestor,
                                                                             AncestorType=Window},
                                              Path=DataContext.SignOutCommand}"
                            CommandParameter="{Binding}"
                            Content="Sign Out" />
                </DataTemplate>
            </DataGridTemplateColumn.CellTemplate>
        </DataGridTemplateColumn>
    </DataGrid.Columns>
</DataGrid>

如果加载示例代码,您会看到所有的“退出”按钮都被禁用,因为在每一行中,CanExecute 方法都接收 null 作为参数,而不是我想要的特定于行的数据。如果此示例工作正常,所有按钮最初都会启用,并且只有在设置了 Exit Time 列中的值后才会禁用。

4

1 回答 1

2

您没有正确设置自定义命令。在您当前的示例中,您不需要手动创建实现 ICommand 的命令,您只需创建一个 Routed 或 RoutedUI 命令并连接适当的处理程序。删除您的 SignOutCommand 对象,然后修改您的 Window 代码,如下所示:

public partial class MainWindow: Window
{
    public ObservableCollection<LogRecord> Records { get; private set; }
    public static RoutedUICommand SignOutCommand { get; private set; }

    public MainWindow()
    {
        InitializeComponent();
        DataContext = this;
        Records = new ObservableCollection<LogRecord>();
        CreateDemoData();

        SignOutCommand = new RoutedUICommand();
        CommandBinding cb = new CommandBinding(SignOutCommand, OnSignOut, OnCanSignOut);
        this.CommandBindings.Add(cb);
    }


    private void CreateDemoData()
    {
        for (int i = 0; i < 5; i++)
        {
            Records.Add(new LogRecord());
        }
    }

    private void OnCanSignOut(object sender, CanExecuteRoutedEventArgs e)
    {
        var record = e.Parameter as LogRecord;
        e.CanExecute = record != null && !record.ExitTime.HasValue;

    }

    private void OnSignOut(object sender, ExecutedRoutedEventArgs e)
    {
        var record = e.Parameter as LogRecord;
        if (record == null) return;
        record.ExitTime = DateTime.Now;
    }
}

然后,修改您的 DataTemplate 如下(基本上,只需从路径中删除 DataContext ):

<dg:DataGridTemplateColumn>
  <dg:DataGridTemplateColumn.CellTemplate>
    <DataTemplate>
       <Button Command="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=Window}, Path=SignOutCommand}" CommandParameter="{Binding}" Content="Sign Out" />
    </DataTemplate>
  </dg:DataGridTemplateColumn.CellTemplate>
</dg:DataGridTemplateColumn>

使用这种方法,您的退出按钮将在设置 DataContext 时正确启用。

于 2012-06-29T21:19:30.350 回答