棱镜中的CanExecute DelegateCommand不起作用

时间:2018-09-11 08:40:14

标签: xamarin.forms xamarin.android prism

当我在短时间内几次单击按钮时,每次都会调用该方法,并且我的应用程序崩溃(当代码尝试导航到另一个页面时)。仅在Xamarin.Android(iOS处理双击)中出现此问题

public bool IsBusy { get; set; }

private DelegateCommand<string> _eventDetailsCommand;
public DelegateCommand<string> EventDetailsCommand => _eventDetailsCommand ?? (_eventDetailsCommand = new DelegateCommand<string>(EventDetails, (x) => !IsBusy));

private void EventDetails(string obj)
{
    IsBusy = true;
    await _navigationService.NavigateAsync("AnotherPage");
    IsBusy = false;
}

Xamarin.Android

棱镜:7.1.0.172(上)

PropertyChanged.Fody(2.2.6):

3 个答案:

答案 0 :(得分:0)

  

private void EventDetails(string obj)   {       IsBusy = true;       ....       IsBusy = false;   }

这会在整个事件处理程序执行期间阻塞UI线程,因此IsBusy = true;IsBusy = false;都不会对ui产生任何可观察的影响。

这是async的教科书示例。

您应该这样写:

private async void EventDetails(string obj)
{
    IsBusy = true;

    await ....;

    IsBusy = false;
}

如果....刚好不能使用,请将其包装在Task.Run中:

private async void EventDetails(string obj)
{
    IsBusy = true;

    await Task.Run( () => .... );

    IsBusy = false;
}

答案 1 :(得分:0)

请尝试

_deviceService.BeginInvokeOnMainThread(() =>
    {
        IsBusy = true;
    });
    //await ... long running process.

在上面的代码片段中,_deviceService的类型为IDeviceService,它是通过以下构造函数注入的:

private IDeviceService _deviceService;

/// <summary>
/// Class constructor 
/// </summary>
/// <param name="deviceService"></param>
public MyPageViewModel(IDeviceService deviceService) {
    _deviceService = deviceService;
}

答案 2 :(得分:0)

仅设置IsBusy不起作用,因为IsBusy更改时命令不会得到通知,UI也不通知。您必须使用RaiseCanExecuteChanged通知所有人。更好的方法是在创建DelegateCommand(Fluent API,但注意只能观察到一个属性)后仅使用ObservesCanExecute。它会为您解决这个问题,并将自动调用RaiseCanExecuteChanged。

以下是我通常如何处理的示例:

public MyViewModel(): ViewModelBase
{
    private readonly INavigationService _navigationService;
    public bool IsBusy { get; set; }

    public ICommand ShowEventDetailsCommand { get; private set; }

    public  MyViewModel(INavigationService navService)
    {
        _navigationService = navService;
        ShowEventDetailsCommand = new DelegateCommand<string>(async(obj) => await ExecuteShowEventDetailsCommand(obj)).ObservesCanExecute(() => !IsBusy);
    }

    public async Task ExecuteShowEventDetailsCommand(obj)
    {
        IsBusy = true; // Note this is not thread safe, just for demonstration
        try
        {
            await _navigationService.NavigateAsync(...);
        }
        finally
        {
            IsBusy = false;
        }
    }
}

这通常是我处理此问题的方式。但是请注意,对IsBusy的访问不是线程安全的,因此您应该使用这样的东西。我有一个带有.TryLock,.Unlock和.IsLocked的LockActivityHandler。

if(LockActivityHandler.TryLock())
{
    try
    {
        //DoStuff
    }
    finally
    {
        LockActivityHandler.Unlock();
    }
}

IsLocked可以绑定到UI元素的Enabled属性以禁用它们。即使未禁用它们并且正在执行其他操作,由于TryLock()=> false

,新操作也不会执行

PS:这也记录在文档中,带有更多示例,因此您可以在这里查看:https://prismlibrary.github.io/docs/commanding.html