WPF命令执行排序

时间:2017-04-19 13:12:47

标签: wpf data-binding

我有一个使用MVVM模式的WPF应用程序。我在窗口中有控件绑定到ViewModel中的属性。我有一个Play按钮,它通过ICommand接口实现绑定到Play()方法。当Play()方法逐步执行时,我首先更改一些属性以更改UI以向用户显示应用程序正在运行:

IsPlaying = true;
IsNotPlaying = false;
DurationTimer.Start();

Status = $"Playing: {_playingUrl}";
FilePreview?.FileNameSet(_playingUrl, "");
FilePreview?.FilePlayStart();

按下“播放”按钮时,应通过IsPlaying属性禁用“播放”按钮,并通过IsNotPlaying属性启用“停止”按钮。此外,DurationTimer应该启动(显示计时器)和Status属性。如上所述,这些用于向用户显示事情正在发生,因为FilePreview?.FilePlayStart();是一种阻塞方法,并且UI在处理时锁定。

然而,当按下“播放”按钮时,UI立即锁定,然后,一旦FilePlayStart()方法完成其处理,它就会释放,其他项目将生效。

我错过了什么吗?

2 个答案:

答案 0 :(得分:0)

与大多数UI框架一样,WPF仅从一个线程更新UI(任何从另一个线程更新UI的尝试都会引发System.InvalidOperationException。)

现在,由于UI线程正在忙于执行您的方法,因此它无法同时更新UI。

WPF与"批量生产"代码执行批量后,UI会处理所有更新。在执行结束时,而不是在中间。

因此,如果在Execute方法(或在UI线程上执行的任何其他方法)中设置了40次"可以执行= true","可以执行= false&# 34;,"可以执行= true","可以执行= false",实际上你不会看到Button无法使用和禁用40次。相反,当方法退出时,然后使用最后一个值更新UI。

那么,如何解决这个问题呢? Execute方法应该是异步的。

类似的东西:

public class Command : ICommand
{
    //ICommad implementation and other stuffs
    //...

    public async void Execute(object parameter)
    {
        await DoExecute(parameter);
    }

    private async Task DoExecute(object parameter)
    {
        //do something asynchronously...
    }
}

在您的特定情况下,FilePreview?.FilePlayStart();应该是异步的,您应该将此方法传递给Command。 你可以写一般Command

public class Command : ICommand
{
    //ICommad implementation and other stuffs
    //...

    //pass the execution in the constructor
    public Command(Func<object, Task> execution)
    {
        _execution = execution;
    }

    private Func<object, Task> _execution;

    public async void Execute(object parameter)
    {
        await _execution(parameter);
    }

    private async Task DoExecute(object parameter)
    {
        //do something asynchronously... like await Task.Delay(2000);
    }
}

然后,您可以在Command

的所有者中以这种方式使用它
MyCommand = new Command(async parameter => 
{
    IsPlaying = true;
    IsNotPlaying = false;
    await FilePreview?.FilePlayStartAsync();
});

一旦输入await部分,执行就会传递给另一个线程,并且当前线程(即UI线程)可以自由更新UI,您将看到{{1根据需要启用/禁用。

如果该方法的异步版本不可用,您可以写:

Button

答案 1 :(得分:0)

您无法在UI线程上执行长时间运行操作,因为它会阻止调度程序直到完成处理。

在这种情况下,只需使用async / await释放调度程序并允许消息继续进行。

private async void PlayCommand()
{
    IsPlaying = true;
    IsNotPlaying = false;
    DurationTimer.Start();

    Status = $"Playing: {_playingUrl}";

    await Task.Run(()=>
    {
        FilePreview?.FileNameSet(_playingUrl, "");
        FilePreview?.FilePlayStart();
    });
}