2016-06-21 5 views
1

У меня проблема с MVVM-Light. Я использую версию 5.3.0.0 ...MVVM-Light - RelayCommand CantExecute issue

.xaml

<DockPanel Dock="Top"> 
     <Button Margin="5" VerticalAlignment="Top" HorizontalAlignment="Center" Command="{Binding CancelDownloadCommand}" FontSize="20" 
       Background="Transparent" BorderThickness="2" BorderBrush="{StaticResource AccentColorBrush4}" ToolTip="Cancelar" 
       DockPanel.Dock="Right"> 
      <StackPanel Orientation="Horizontal"> 
       <Image Source="Images/48x48/Error.png" Height="48" Width="48"/> 
       <Label Content="{Binding ToolTip, RelativeSource={RelativeSource AncestorType={x:Type Button}}}" FontFamily="Segoe UI Light"/> 
      </StackPanel> 
     </Button> 
     <Button Margin="5" VerticalAlignment="Top" HorizontalAlignment="Center" Command="{Binding DownloadCommand}" FontSize="20" 
       Background="Transparent" BorderThickness="2" BorderBrush="{StaticResource AccentColorBrush4}" ToolTip="Descargar" 
       DockPanel.Dock="Right"> 
      <StackPanel Orientation="Horizontal"> 
       <Image Source="Images/48x48/Download.png" Height="48" Width="48"/> 
       <Label Content="{Binding ToolTip, RelativeSource={RelativeSource AncestorType={x:Type Button}}}" FontFamily="Segoe UI Light"/> 
      </StackPanel> 
     </Button> 
    </DockPanel> 

DownloadViewModel.cs

Я использовал MessageBox, но в моем случае, вызовите метод, который читает XML. Этот пример не работает, кнопки отключены, но не активируются в конце выполнения. Мне нужно нажать на пользовательский интерфейс, чтобы активировать его.

using GalaSoft.MvvmLight; 
using GalaSoft.MvvmLight.CommandWpf; 

private async void Download() 
{ 
    Reset(); 

    await Task.Run(() => 
    { 
     MessageBox.Show("Hello"); 
    }); 

    Reset(); 
} 

private void Reset() 
{ 
    IsEnabled = !IsEnabled; 
    IsEnabledCancel = !IsEnabledCancel; 
} 

private ICommand _downloadCommand; 
public ICommand DownloadCommand 
{ 
    get { return _downloadCommand ?? (_downloadCommand = new RelayCommand(Download,() => IsEnabled)); } 
} 

private ICommand _cancelDownloadCommand; 
public ICommand CancelDownloadCommand 
{ 
    get 
    { 
     return _cancelDownloadCommand ?? 
       (_cancelDownloadCommand = new RelayCommand(CancelDownload,() => IsEnabledCancel)); 
    } 
} 

private bool _isEnabled = true; 
private bool IsEnabled 
{ 
    get { return _isEnabled; } 
    set 
    { 
     if (_isEnabled != value) 
     { 
      _isEnabled = value; 
      RaisePropertyChanged(); 
     } 
    } 
} 

private bool _isEnabledCancel; 
private bool IsEnabledCancel 
{ 
    get { return _isEnabledCancel; } 
    set 
    { 
     if (_isEnabledCancel != value) 
     { 
      _isEnabledCancel = value; 
      RaisePropertyChanged(); 
     } 
    } 
} 

При использовании CommandManager.InvalidateRequerySuggested(), я установил ее. Но читайте где-нибудь, что не рекомендуется, потому что эта команда проверяет все RelayCommand. Раньше этого не было.

Но если в пределах Задачи. Не добавляйте ничего. Он работает отлично. Кнопки активируются и деактивируются снова.

private async void Download() 
{ 
    Reset(); 

    await Task.Run(() => 
    { 
     // WIDTHOUT CODE 
     // WIDTHOUT CODE 
     // WIDTHOUT CODE 
    }); 

    Reset(); 
} 

ответ

0

Одна вещь, которую я действительно замечал, что ваши Enabled Properties (IsEnabled, IsEnabledCancel) являются private, когда они должны быть public. Однако это не исправить вашу проблему :)

Простое исправление, чтобы избавиться от CanExecute части вашего командования например

public ICommand DownloadCommand 
{ 
    get { return _downloadCommand ?? (_downloadCommand = new RelayCommand(Download)); } 
} 

и привязку к собственности на Button.IsEnabled собственности в XAML например

<Button IsEnabled="{Binding IsEnabled}" Margin="5" VerticalAlignment="Top" 
     HorizontalAlignment="Center" Command="{Binding DownloadCommand}" 
     FontSize="20" Background="Transparent" BorderThickness="2" 
     BorderBrush="Red" ToolTip="Descargar" DockPanel.Dock="Right"> 
    ... 
</Button> 

Надежда, что помогает

1

При обновлении CanExecute, в вашем случае IsEnabled и IsEnabledCancel объектов, вам нужно поднять CanExecuteChanged.

Еще больше вы можете немного упростить свой код.

private bool _isEnabled; 

public bool IsEnabled 
{ 
    get { return _isEnabled; } 
    set 
    { 
     if (Set(ref _isEnabled, value)) 
     { 
      DownloadCommand.RaiseCanExecuteChanged(); 
     } 
    } 
} 

Точно так же обновите свой IsEnabledCancel.

Конечно, вы должны объявить свою команду как RelayCommand, а не ICommand.

private RelayCommand _downloadCommand; 

public RelayCommand DownloadCommand 
{ 
    get { return _downloadCommand ?? (_downloadCommand = new RelayCommand(Download,() => IsEnabled)); } 
} 

Вы также можете прочитать: "A smart MVVM command".

1

Глядя на source code for MVVM Light, он основан на шаблоне CommandManager.InvalidateRequerySuggested() (anti). Который вы справедливо говорите, это массивный боевик, из-за глобального характера (анти) шаблона.

Проблема заключается в конструкторе.public RelayCommand(Action execute, Func<bool> canExecute)

С canExecute будучи Func<bool>, это невозможно, чтобы быть в состоянии получить (во время выполнения) имя свойства, и поэтому невозможно связать на INotifyPropertyChanged.PropertyChanged с событием. Таким образом, команда переучет canExecute.

@kubakista показывает вам, как заставить переоценку, вызвав метод RaiseCanExecuteChanged. Но это действительно нарушает принцип единой ответственности и препятствует реализации ICommand.

Мой совет заключается в использовании ReactiveUIReactiveCommand. Это позволяет:

DownloadCommand = ReactiveCommand.Create(Download, this.WhenAnyValue(x => x.IsEnabled).Select(enabled => enabled)); 
CancelDownloadCommand = ReactiveCommand.Create(CancelDownload, this.WhenAnyValue(x => x.IsEnabled).Select(enabled => false == enabled)); 


public bool IsEnabled 
{ 
    get {return _isEnabled; } 
    set 
    { 
     _isEnabled = value; 
     OnPropertyChanged(); 
    } 
}