简体   繁体   English

为什么带有ICommand绑定的按钮在单击时没有立即显示为禁用状态?

[英]Why does my button with an ICommand binding not immediately appear disabled when clicked?

I have a simple WPF program with an ICommand . 我有一个带ICommand的简单WPF程序。 I am finding that the button doesn't enable/disable as I would expect. 我发现按钮没有启用/禁用,正如我期望的那样。 I can illustrate this best with a contrived code example: 我可以用一个人为的代码示例来最好地说明这一点:

class Reload : ICommand
{
    private readonly BackgroundWorker _bworker = new BackgroundWorker();

    public Reload()
    {
        this._isExecuting = false;

        this._bworker.DoWork += this._bworker_DoWork;
        this._bworker.RunWorkerCompleted += this._bworker_RunWorkerCompleted;
    }

    public event EventHandler CanExecuteChanged;
    private void OnCanExecuteChanged()
    {
        if (this.CanExecuteChanged != null)
            this.CanExecuteChanged(this, EventArgs.Empty);
    }

    private bool _isExecuting;
    private void SetIsExecuting(bool isExecuting)
    {
        this._isExecuting = isExecuting;
        this.OnCanExecuteChanged();
    }

    public bool CanExecute(object parameter)
    {
        return !this._isExecuting;
    }

    public void Execute(object parameter)
    {
        //this does not update the GUI immediately
        this.SetIsExecuting(true);

        //This line doesn't fix my problem
        CommandManager.InvalidateRequerySuggested();

        //during this wait, button appears "clicked"
        Thread.Sleep(TimeSpan.FromSeconds(2)); //simulate first calculation

        this._bworker.RunWorkerAsync();
    }

    private void _bworker_DoWork(object sender, DoWorkEventArgs e)
    {
        //during this wait, button appears disabled
        Thread.Sleep(TimeSpan.FromSeconds(2)); //simulate second calculation
    }

    private void _bworker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
    {
        //this updates immediately
        this.SetIsExecuting(false);
    }
}

In the Execute(object) method, I trigger the CanExecuteChanged event in a way that will cause CanExecute(object) to return false. Execute(object)方法中,我以导致CanExecute(object)返回false的方式触发CanExecuteChanged事件。 After that call, I expect the button to become disabled immediately, but it doesn't become disabled until some point between the call to RunWorkerAsync() and the second simulated calculation. 在该调用之后,我希望该按钮会立即被禁用,但是直到调用RunWorkerAsync()与第二个模拟计算之间的某个时刻它才会被禁用。

In the background worker's RunWorkerCompleted(...) event handler, I again trigger the CanExecuteChanged event, but this time in a way that will cause CanExecuteChanged(object) to return true. 在后台工作者的RunWorkerCompleted(...)事件处理程序中,我再次触发CanExecuteChanged事件,但这一次将导致CanExecuteChanged(object)返回true。 After this call, the button immediately becomes enabled. 通话后,该按钮将立即启用。

Why does the button not immediately appear as disabled when I trigger the CanExecuteChanged event? 为什么触发CanExecuteChanged事件时按钮没有立即显示为禁用状态?

Note #1: that the first simulated calculation represents code that I have that should run on the main GUI thread. 注意#1:第一个模拟计算代表我拥有的应在主GUI线程上运行的代码。 If I remove this call, the button acts as I would expect. 如果我删除此呼叫,该按钮将按我期望的那样工作。

Note #2: I've read about using CommandManager.InvalidateRequerySuggested() to force the code to call the CanExecute(object) method. 注意#2:我已经阅读了有关使用CommandManager.InvalidateRequerySuggested()强制代码调用CanExecute(object)方法的信息。 I've shown in my comments that this isn't working for me. 我在评论中显示这对我不起作用。 Considering that I call OnCanExecuteChanged(...) , I think that that suggestion is redundant anyway. 考虑到我调用OnCanExecuteChanged(...) ,我认为该建议是多余的。

The right solution is the one you've already found, move the first long-running operation off of the UI thread. 正确的解决方案是您已经找到的解决方案,将第一个长时间运行的操作移出UI线程。

However, if you can't do that, the problem is that you aren't giving the UI a chance to run its binding and update the state. 但是,如果您不能这样做,那么问题就在于您没有给UI提供运行其绑定和更新状态的机会。 It probably updates as soon as the background worker starts (because control is returned from your function). 后台工作程序启动后,它可能会立即更新(因为从函数中返回了控件)。

You could take advantage of async/await and Task.Delay to give up some time for the UI to update: 您可以利用async/awaitTask.Delay浪费一些时间来更新UI:

public async void Execute(object parameter)
{
    //this does not update the GUI immediately
    this.SetIsExecuting(true);

    //Delays this function executing, gives the UI a chance to pick up the changes
    await Task.Delay(500);

    //during this wait, button appears "clicked"
    Thread.Sleep(TimeSpan.FromSeconds(2)); //simulate first calculation

    this._bworker.RunWorkerAsync();
}

Async/Await allows you to execute an operation asynchronously, and wait for it to finish, while allowing the current thread to continue executing (outside of the current method call). 异步/等待允许您异步执行操作,并等待操作完成,同时允许当前线程继续执行(当前方法调用之外)。 Its not super easy to explain all the technical details, see the link for more info. 解释所有技术细节并非易事,请参阅链接以获取更多信息。

I would wait at least 20ms, and probably 50ms. 我至少要等待20毫秒,可能要等50毫秒。 Obviously delaying like this isn't the cleanest solution, but without removing the Sleep (or moving the code it represents off the UI thread) your options are pretty limited. 显然,这样的延迟并不是最干净的解决方案,但是如果不删除Sleep (或将其表示的代码移出UI线程),则您的选择将受到很大限制。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 将ICommand绑定到按钮? - Binding ICommand to button? ItemControl中的ICommand Button绑定 - ICommand Button binding in Itemcontrol 为什么单击按钮时我的应用程序会崩溃? - Why does my app crash when a button is clicked? 为什么在单击 tabcontrol 内的 WPF 文本框时,Windows 10 键盘会出现并立即消失? - Why does windows 10 keyboard appear and immediately dissapear when clicking in WPF textbox inside tabcontrol? WPF MVVM-带文本框的简单绑定按钮(Icommand) - WPF MVVM - Simple Binding Button with Textbox (Icommand) 绑定静态ICommand Variable变灰按钮? - Binding Static ICommand Variable grays out button? 在按钮单击处理程序中设置数据上下文时,为什么数据绑定不起作用? - Why does the data binding not work when i set the data context in my button click handler? 当我将CommandParameter设置为某个Binding时,为什么我的ICommand.CanExecute(object)参数为null,但当我将其设置为某个静态值时为非null? - Why is my ICommand.CanExecute(object) parameter null when I set CommandParameter to some Binding, but non-null when I set it to some static value? 绑定超链接时遇到麻烦在我的视图中命令ICommand - Trouble binding a hyperlink Command an ICommand in my view 将menuitem IsEnabled绑定到ICommand CanExecute时出现ArgumentNullException - ArgumentNullException when binding menuitem IsEnabled to ICommand CanExecute
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM