在较长的运行过程中禁用 WPF 按钮,MVVM 方式 [英] Disable WPF buttons during longer running process, the MVVM way

查看:14
本文介绍了在较长的运行过程中禁用 WPF 按钮,MVVM 方式的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个 WPF/MVVM 应用程序,它由一个带有几个按钮的窗口组成.
每个按钮都会触发对外部设备的调用(USB 导弹发射器),这需要几秒钟.

I have a WPF/MVVM app, which consists of one window with a few buttons.
Each of the buttons triggers a call to an external device (an USB missile launcher), which takes a few seconds.

当设备运行时,GUI 被冻结.
(这没问题,因为该应用的唯一目的是调用 USB 设备,而在设备移动的过程中,您无论如何也不能做任何其他事情!)

While the device is running, the GUI is frozen.
(This is okay, because the only purpose of the app is to call the USB device, and you can't do anything else anyway while the device is moving!)

唯一有点难看的是,当设备移动时,冻结的 GUI 仍然接受额外的点击.
当设备仍然移动并且我第二次点击同一个按钮时,设备会在第一次运行"完成后立即再次开始移动.

The only thing that's a bit ugly is that the frozen GUI still accepts additional clicks while the device is moving.
When the device still moves and I click on the same button a second time, the device immediately starts moving again as soon as the first "run" is finished.

所以我想在单击一个按钮后立即禁用 GUI 中的所有按钮,并在按钮的命令运行完成后再次启用它们.

So I'd like to disable all the buttons in the GUI as soon as one button is clicked, and enable them again when the button's command has finished running.

我找到了一个看起来符合 MVVM 的解决方案.
(至少对我来说......请注意,我仍然是 WPF/MVVM 初学者!)

I have found a solution for this that looks MVVM-conform.
(at least to me...note that I'm still a WPF/MVVM beginner!)

问题是当我调用与 USB 设备通信的外部库时,此解决方案不起作用(例如:按钮未禁用).
但是禁用 GUI 的实际代码是正确的,因为当我用 MessageBox.Show() 替换外部库调用时,它确实工作.

The problem is that this solution doesn't work (as in: the buttons are not disabled) when I call the external library that communicates with the USB device.
But the actual code to disable the GUI is correct, because it does work when I replace the external library call by MessageBox.Show().

我构建了一个重现问题的最小工作示例(完整演示项目在这里):

I've constructed a minimal working example that reproduces the problem (complete demo project here):

这是视图:

<Window x:Class="WpfDatabindingQuestion.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525">
    <Grid>
        <StackPanel>
            <Button Content="MessageBox" Command="{Binding MessageCommand}" Height="50"></Button>
            <Button Content="Simulate external device" Command="{Binding DeviceCommand}" Height="50" Margin="0 10"></Button>
        </StackPanel>
    </Grid>
</Window>

...这是 ViewModel (使用 RelayCommand 来自 Josh Smith 的 MSDN 文章):

...and this is the ViewModel (using the RelayCommand from Josh Smith's MSDN article):

using System.Threading;
using System.Windows;
using System.Windows.Input;

namespace WpfDatabindingQuestion
{
    public class MainWindowViewModel
    {
        private bool disableGui;

        public ICommand MessageCommand
        {
            get
            {
                return new RelayCommand(this.ShowMessage, this.IsGuiEnabled);
            }
        }

        public ICommand DeviceCommand
        {
            get
            {
                return new RelayCommand(this.CallExternalDevice, this.IsGuiEnabled);
            }
        }

        // here, the buttons are disabled while the MessageBox is open
        private void ShowMessage(object obj)
        {
            this.disableGui = true;
            MessageBox.Show("test");
            this.disableGui = false;
        }

        // here, the buttons are NOT disabled while the app pauses
        private void CallExternalDevice(object obj)
        {
            this.disableGui = true;
            // simulate call to external device (USB missile launcher),
            // which takes a few seconds and pauses the app
            Thread.Sleep(3000);
            this.disableGui = false;
        }

        private bool IsGuiEnabled(object obj)
        {
            return !this.disableGui;
        }
    }
}

我怀疑打开 MessageBox 会在后台触发一些在我调用外部库时不会发生的事情.
但我找不到解决办法.

I'm suspecting that opening a MessageBox triggers some stuff in the background that does not happen when I just call an external library.
But I'm not able to find a solution.

我也试过:

  • 实现INotifyPropertyChanged(并使this.disableGui成为一个属性,并在更改时调用OnPropertyChanged)
  • 到处调用CommandManager.InvalidateRequerySuggested()
    (我在 SO 上类似问题的几个答案中发现了这一点)
  • implementing INotifyPropertyChanged (and making this.disableGui a property, and calling OnPropertyChanged when changing it)
  • calling CommandManager.InvalidateRequerySuggested() all over the place
    (I found that in several answers to similar problems here on SO)

有什么建议吗?

推荐答案

试试这个:

//Declare a new BackgroundWorker
BackgroundWorker worker = new BackgroundWorker();
worker.DoWork += (o, ea) =>
{
    try
    {
        // Call your device

        // If ou need to interact with the main thread
       Application.Current.Dispatcher.Invoke(new Action(() => //your action));
    }
    catch (Exception exp)
    {
    }
};

//This event is raise on DoWork complete
worker.RunWorkerCompleted += (o, ea) =>
{
    //Work to do after the long process
    disableGui = false;
};

disableGui = true;
//Launch you worker
worker.RunWorkerAsync();

这篇关于在较长的运行过程中禁用 WPF 按钮,MVVM 方式的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆