MVVM-Light - RelayCommand CantExecute问题

时间:2016-06-21 14:20:26

标签: wpf mvvm-light

我遇到MVVM-Light问题。我使用版本5.3.0.0 ...

.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的方法。此示例不起作用,按钮被禁用,但在执行结束时不会重新激活。我需要点击UI激活。

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

3 个答案:

答案 0 :(得分: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)); }
}

您还可以阅读:&#34; A smart MVVM command&#34;。

答案 1 :(得分:1)

查看source code for MVVM Light,它基于CommandManager.InvalidateRequerySuggested()(反)模式。由于(反)模式的全球性,你正确地说这是一场巨大的表现。

问题在于构造函数。 public RelayCommand(Action execute, Func<bool> canExecute)

如果canExecuteFunc<bool>,则无法(在运行时)获取属性名称,因此无法绑定INotifyPropertyChanged.PropertyChanged事件。从而导致命令重新评估canExecute

@kubakista向您展示如何通过调用RaiseCanExecuteChanged方法强制重新评估。但这确实打破了单一责任原则,并泄漏了ICommand的实施。

我的建议是使用ReactiveUI的{​​{1}}。这允许你这样做:

ReactiveCommand

答案 2 :(得分:0)

我注意到的一件事是,Enabled属性(IsEnabled,IsEnabledCancel)属private时应为public。但是,这不能解决您的问题:)

一个简单的解决方法是摆脱命令的CanExecute部分 例如

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

并绑定到xaml中Button.IsEnabled属性上的属性 例如

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

希望有所帮助