Disable WPF buttons during longer running process, the MVVM way

后端 未结 4 453
遇见更好的自我
遇见更好的自我 2020-12-16 13:12

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

相关标签:
4条回答
  • 2020-12-16 13:32

    Ok the CanExecute method will not work because the click will immediately put you into your long-running task.
    So here's how I would do it:

    1. Make your view model implement INotifyPropertyChanged

    2. Add a property called something like:

      public bool IsBusy
      {
          get
          {
              return this.isBusy;
          }
          set
          { 
              this.isBusy = value;
              RaisePropertyChanged("IsBusy");
          }
      }
      
    3. Bind your buttons to this property in this manner:

      <Button IsEnabled="{Binding IsBusy}" .. />
      
    4. In your ShowMessage/CallExternal device methods add the line

      IsBusy = true;
      

    Should do the trick

    0 讨论(0)
  • 2020-12-16 13:33

    Try this:

    //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();
    
    0 讨论(0)
  • 2020-12-16 13:46

    I think this is a bit more elegant:

    XAML:

    <Button IsEnabled="{Binding IsGuiEnabled}" Content="Simulate external device" Command="{Binding DeviceCommand}" Height="50" Margin="0 10"></Button>
    

    C# (using async & await):

    public class MainWindowViewModel : INotifyPropertyChanged
    {
        private bool isGuiEnabled;
    
        /// <summary>
        /// True to enable buttons, false to disable buttons.
        /// </summary>
        public bool IsGuiEnabled 
        {
            get
            {
                return isGuiEnabled;
            }
            set
            {
                isGuiEnabled = value;
                OnPropertyChanged("IsGuiEnabled");
            }
        }
    
        public ICommand DeviceCommand
        {
            get
            {
                return new RelayCommand(this.CallExternalDevice, this.IsGuiEnabled);
            }
        }
    
        private async void CallExternalDevice(object obj)
        {
            IsGuiEnabled = false;
            try
            {
                await Task.Factory.StartNew(() => Thread.Sleep(3000));
            }
            finally
            {
                IsGuiEnabled = true; 
            }
        }
    }
    
    0 讨论(0)
  • 2020-12-16 13:50

    Because you run CallExternalDevice() on the main thread, the main thread won't have time to update any UI until that job is done, which is why the buttons remain enabled. You could start your long-running operation in a separate thread, and you should see that the buttons are disabled as expected:

    private void CallExternalDevice(object obj)
    {
        this.disableGui = true;
    
        ThreadStart work = () =>
        {
            // simulate call to external device (USB missile launcher),
            // which takes a few seconds and pauses the app
            Thread.Sleep(3000);
    
            this.disableGui = false;
            Application.Current.Dispatcher.BeginInvoke(new Action(() => CommandManager.InvalidateRequerySuggested()));
        };
        new Thread(work).Start();
    }
    
    0 讨论(0)
提交回复
热议问题