2016-06-21 139 views
1

MVVM-Light存在問題。我使用的版本5.3.0.0 ...MVVM-Light - RelayCommand CantExecute問題

.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。這在我之前沒有發生過。

但是如果在Task.Run內沒有添加任何東西。它完美的作品。按鈕被激活並再次禁用。

private async void Download() 
{ 
    Reset(); 

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

    Reset(); 
} 

回答

0

有一兩件事我注意到的是,你的Enabled屬性(的IsEnabled,IsEnabledCancel)是private時,他們應該是public。但是這並不能解決您的問題:)

一個簡單的解決方法是擺脫你的命令 如

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

CanExecute部分並綁定到你的財產的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,在你的情況下IsEnabledIsEnabledCancel屬性,你必須提高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()(反)模式。由於(反)模式的全球性質,你正確地說是一個巨大的表現。

問題在於構造函數。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(); 
    } 
}