CanExecute в Prism DelegateCommand не работает - PullRequest
0 голосов
/ 11 сентября 2018

Когда я нажимаю кнопку несколько раз за короткий промежуток времени, метод вызывается каждый раз, и мое приложение вылетает (когда код пытается перейти на другую страницу). Эта проблема возникает только в 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 голосов
/ 11 сентября 2018

Пожалуйста, попробуйте

_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;
}
0 голосов
/ 16 января 2019

Просто установка IsBusy не работает, потому что команда не получает уведомления при изменении IsBusy, как и пользовательский интерфейс. Вы должны использовать RaiseCanExecuteChanged, чтобы уведомить всех. Лучше всего просто использовать ObservedCanExecute после создания DelegateCommand (Fluent API, но обратите внимание, что можно наблюдать только одно свойство). Он позаботится об этом за вас и автоматически вызовет 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 не является потокобезопасным, поэтому вы должны использовать что-то, что есть. У меня есть что-то вроде LockActivityHandler с .TryLock, .Unlock и .IsLocked.

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

IsLocked можно привязать к свойству Enabled элементов пользовательского интерфейса, чтобы отключить их. Даже если они не отключены и выполняется другое действие, новое действие не будет выполнено из-за TryLock () => false

PS: Это также записано в документации с большим количеством примеров, поэтому вы можете посмотреть здесь: https://prismlibrary.github.io/docs/commanding.html

0 голосов
/ 11 сентября 2018

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

Блокирует поток пользовательского интерфейса на все время выполнения обработчика событий, поэтому ни IsBusy = true;, ни IsBusy = false; не оказывают заметного влияния на пользовательский интерфейс.

Это пример из учебника для 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;
}
...