2012-06-29 3 views
1

CanExecute 메서드로 작업 명령 속성을 가져 오는 데 문제가 있습니다. DataGrid 내부에있는 버튼에 명령을 바인딩했습니다. CommandParameter를 DataGrid의 행에 대한 레코드 일 수있는 버튼의 DataContext에 바인딩했습니다.DataContext가 변경 될 때 WPF 명령 CanExecute가 다시 평가되지 않음

CommandParameter 바인딩이 변경 될 때 CanExecute 메서드를 다시 평가해야합니다.이 경우 행의 DataContext 속성이 설정됩니다. 그러나 행 데이터에 대해 CanExecute 메서드를 평가하는 대신 행이 DataContext를 가져 오기 전에 CanExecute 메서드가 평가되고 DataContext가 업데이트 된 후에 다시 계산되지 않는 것처럼 보입니다.

각 행의 DataContext에 대해 내 명령의 CanExecute 메서드를 평가하는 방법을 알려주시겠습니까?

문제점을 설명하기 위해 샘플 응용 프로그램을 만들었습니다. 여기에 코드입니다 :

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

MainWindow.xaml의 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 
} 

XAML의 코드 숨김

당신은로드하는 경우 예제 코드에서는 모든 행 아웃에서 CanEx ecute 메서드는 원하는 행 특정 데이터 대신 매개 변수로 null을받습니다. 이 샘플이 제대로 작동하면 처음에는 모든 버튼이 활성화되며 종료 시간 열의 값을 설정 한 후에 만 ​​비활성화됩니다.

답변

2

사용자 지정 명령을 올바르게 설정하지 않았습니다. 현재 예제에서는 ICommand를 구현하는 명령을 수동으로 생성 할 필요가 없으며 Routed 또는 RoutedUI 명령을 작성하고 적절한 핸들러를 연결하기 만하면됩니다. 다음

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

을 다음과 같이 (기본적으로 그냥 경로에서 DataContext를 제거) 당신의 DataTemplate을 수정 : 당신의 SignOutCommand 개체를 제거한 다음, 다음과 같이 당신의 창 코드를 수정

<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가 설정되면 로그 아웃 단추가 올바르게 활성화됩니다.

+0

Perfect. 고맙습니다! –

관련 문제