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 を受け取っているため、すべての [サインアウト] ボタンが無効になっていることがわかります。このサンプルが正常に機能していれば、最初はすべてのボタンが有効になり、[終了時間] 列の値が設定された後にのみ無効になります。

4

1 に答える 1

2

カスタムコマンドが正しく設定されていません。現在の例では、ICommandを実装するコマンドを手動で作成する必要はありません。単に、RoutedまたはRoutedUIコマンドを作成し、適切なハンドラーを接続する必要があります。SignOutCommandオブジェクトを削除してから、次のようにウィンドウコードを変更します。

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 に答える