2016-06-21 140 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(); 
    } 
}