отключить кнопку WPF перед процессом и включить после завершения процесса в WPF MVVM - PullRequest
0 голосов
/ 05 июня 2019

У меня есть кнопка, которая выполняет некоторую обработку, и мне нужно отключить ее до запуска процесса и включить ее после завершения процесса. Мне нужно сделать это в шаблоне mvvm.

MainWindow.xaml

<Window x:Class="ButtonCommandBindingMVVM.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:local="clr-namespace:ButtonCommandBindingMVVM"
    mc:Ignorable="d"
    Title="MainWindow" Height="450" Width="800">
<Window.Resources>
    <local:ViewModel x:Key="vm"/>
</Window.Resources>
<Grid>
    <Grid.RowDefinitions>
        <RowDefinition Height="40"/>
        <RowDefinition Height="40"/>
        <RowDefinition Height="40"/>
        <RowDefinition Height="*"/>
        <RowDefinition Height="40"/>
    </Grid.RowDefinitions>
    <Button Grid.Row="1"
            x:Name="Button1"
            Width="100"
            Height="27"
            Content="Say Hello"
            Command="{Binding Button1_ClickCommand, Source={StaticResource vm}}"
            />
    <Button Grid.Row="2"
            x:Name="Button2"
            Width="100"
            Height="27"
            Content="Say Welcome"
            Command="{Binding Button2_ClickCommand, Source={StaticResource vm}}"
            />

</Grid>

Command.cs Это класс команд реле.

class Command : ICommand
{
    Action<object> ExecuteMethod;

    Func<object, bool> CanExecuteMethod;

    public Command(Action<object> ExecuteMethod, Func<object, bool> CanExecuteMethod)
    {
        this.ExecuteMethod = ExecuteMethod;
        this.CanExecuteMethod = CanExecuteMethod;
    }

    public bool CanExecute(object parameter)
    {
        return true;
    }

    public void Execute(object parameter)
    {
        ExecuteMethod(parameter);
    }

    public event EventHandler CanExecuteChanged;

}

ViewModel.cs

public class ViewModel
{
    public ICommand Button1_ClickCommand { get; set; }
    public ICommand Button2_ClickCommand { get; set; }

    public ViewModel()
    {
        Button1_ClickCommand = new Command(ExecuteMethodButton1_ClickCommand, CanExecuteMethodButton1_ClickCommand);
        Button2_ClickCommand = new Command(ExecuteMethodButton2_ClickCommand, CanExecuteMethodButton2_ClickCommand);
    }

    private bool CanExecuteMethodButton1_ClickCommand(object parameter)
    {
        return true;
    }

    private void ExecuteMethodButton1_ClickCommand(object parameter)
    {
        await Task.Run(() =>
        {
            Thread.Sleep(5000);
        });
        MessageBox.Show("Hello");
    }

    private bool CanExecuteMethodButton2_ClickCommand(object parameter)
    {
        return true;
    }

    private void ExecuteMethodButton2_ClickCommand(object parameter)
    {
        MessageBox.Show("Welcome");
    }
}

Отключить Button1 и включить его после того, как нить спит в течение 5 секунд.

Ответы [ 2 ]

0 голосов
/ 05 июня 2019

Чрезвычайно простая реализация асинхронной ICommand - которая отключает цель команды только во время ее асинхронного выполнения - может выглядеть следующим образом:

public class AsyncCommand : ICommand
{
    private readonly Func<object, Task> execute;
    private bool canExecute = true;

    public event EventHandler CanExecuteChanged;

    public AsyncCommand(Func<object, Task> execute)
    {
        this.execute = execute;
    }

    public bool CanExecute(object parameter)
    {
        return canExecute;
    }

    public async void Execute(object parameter)
    {
        if (canExecute)
        {
            canExecute = false;
            CanExecuteChanged?.Invoke(this, EventArgs.Empty);

            try
            {
                await execute(parameter);
            }
            finally
            {
                canExecute = true;
                CanExecuteChanged?.Invoke(this, EventArgs.Empty);
            }
        }
    }
}

Вы можете протестировать ее с помощью команды, подобной этой:

public ICommand TestCommand { get; }

private Task TestMethod(object p)
{
    return Task.Delay(1000);
}

...
TestCommand = new AsyncCommand(TestMethod);

Конечно, метод execute также может быть объявлен async:

private async Task TestMethod(object p)
{
    // do something before awaiting a long running task

    await Task.Run(() =>
    {
        // a long running task
    });

    // do something after awaiting a long running task
}
0 голосов
/ 05 июня 2019

Попробуйте эту реализацию ICommand, которая работает асинхронно.

public class perRelayCommandAsync : ObservableObject, ICommand
{
    private readonly Func<Task> _execute;
    private readonly Func<bool> _canExecute;

    public perRelayCommandAsync(Func<Task> execute) : this(execute, () => true ) { }

    public perRelayCommandAsync(Func<Task> execute, Func<bool> canExecute)
    {
        _execute = execute ?? throw new ArgumentNullException(nameof(execute));
        _canExecute = canExecute;
    }

    private bool _isExecuting;

    public bool IsExecuting
    {
        get => _isExecuting;
        set
        {
            if(Set(nameof(IsExecuting), ref _isExecuting, value))
                RaiseCanExecuteChanged();
        }
    }

    public event EventHandler CanExecuteChanged;

    public bool CanExecute(object parameter) => !IsExecuting 
                                                && (_canExecute == null || _canExecute());

    public async void Execute(object parameter)
    {
        if (!CanExecute(parameter))
            return;

        IsExecuting = true;
        try
        {
            await _execute().ConfigureAwait(true);
        }
        finally
        {
            IsExecuting = false;
        }
    }

    public void RaiseCanExecuteChanged() => CanExecuteChanged?.Invoke(this, EventArgs.Empty);
}

ObservableObject взят из MVVMLight, но вы можете заменить его любой реализацией INotifyPropertyChanged. Флаг _canExecute приведет к автоматическому отключению кнопки во время выполнения Задачи.

Ваш пример станет

Button1_ClickCommand = new perRelayCommandAsync(()=>Task.Delay(TimeSpan.FromSeconds(5));

Не используйте Thread.Sleep () в асинхронном коде - вы просто блокируете исполняющий поток.

Более широкое обсуждение команд WPF смотрите в моем блоге .

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...