Как добавить задержку в программу WPF без блокировки пользовательского интерфейса

Я создаю эмулятор устройства. Когда он запускается, требуется некоторое время для его инициализации. Это было бы логически представлено включением и немедленным переходом в состояние «Инициализация», а через некоторое время - в состояние «Готов».

Я использую MVVM, поэтому ViewModel пока представляет всю логику устройства. Каждое из возможных состояний имеет стиль с запуском по данным, который должен отображаться в представлении. Если я просто установлю состояние при построении модели представления, представление будет отображаться с правильным внешним видом.

Я хочу создать «состояние тайм-аута», то есть, когда происходит какое-то событие (запуск приложения, нажатие определенной кнопки), устройство переходит в состояние на фиксированное время, а затем возвращается в состояние «готово». ", или состояние" простоя ".

Я думал использовать Sleep, но сон блокирует UI (так говорят). Поэтому я думаю об использовании потоков, но не знаю, как это сделать. Вот что у меня получилось:

using System.ComponentModel;

namespace EmuladorMiotool {
    public class MiotoolViewModel : INotifyPropertyChanged {
        Estados _estado;

        public Estados Estado {
          get {
              return _estado;
          }
          set {
              _estado = value;
              switch (_estado) {
                  case Estados.WirelessProcurando:
                      // WAIT FOR TWO SECONDS WITHOUT BLOCKING GUI
                      // It should look like the device is actually doing something
                      // (but not indeed, for now)
                      _estado = Estados.WirelessConectado;
                      break;
              }
              RaisePropertyChanged("Estado");
          }
        }

        public MiotoolViewModel() {
            // The constructor sets the initial state to "Searching"
            Estado = Estados.WirelessProcurando;
        }

        public event PropertyChangedEventHandler PropertyChanged;
        protected virtual void RaisePropertyChanged(string propertyName) {
            PropertyChangedEventHandler handler = PropertyChanged;
            if (handler != null)
                handler(this, new PropertyChangedEventArgs(propertyName));
        }

    }

    public enum Estados {
        UsbOcioso,
        UsbAquisitando,
        UsbTransferindo,
        WirelessNãoPareado,
        WirelessPareado,
        WirelessDesconectado,
        WirelessProcurando,
        WirelessConectado,
        WirelessAquisitando,
        DataLoggerOcioso,
        DataLoggerAquisitando,
        Erro,
        Formatando
    }
}

person heltonbiker    schedule 23.04.2013    source источник


Ответы (1)


Во-первых, наличие операции Sleep / Async в свойстве (getter / setter) считается плохой практикой.

Попробуйте это как замену Sleep без блокировки потока пользовательского интерфейса:

Создайте функцию, чтобы установить Estado в Estados.WirelessProcurando

Предполагается, что WirelessProcurando означает инициализацию, а WirelessConectado означает инициализацию

.net45:

private async Task SetWirelessProcurando(int milliSeconds) {
  Estado = Estados.WirelessProcurando;
  await Task.Delay(milliSeconds);
  Estado = Estados.WirelessConectado;
}

Причина, по которой у нас есть функция, возвращающая Task vs void, заключается в том, чтобы позволить вызывающей стороне, если требуется, await для этой функции, если логика требует этого соответственно

Если вы не можете использовать await:

private void SetWirelessProcurando(int milliSeconds) {
  Estado = Estados.WirelessProcurando;
  var tempTask = new Task
    (
    () => {
      Thread.Sleep(milliSeconds);
      System.Windows.Application.Current.Dispatcher.BeginInvoke(new Action(() => Estado = Estados.WirelessConectado));
    },
    System.Threading.Tasks.TaskCreationOptions.LongRunning
    );
  tempTask.Start();
}

Теперь вызов этой функции всякий раз, когда вы хотите изменить сеттер, немедленно установит состояние на «Intiialising» и после заданного milliSeconds переключится в состояние Initialised.

person Viv    schedule 23.04.2013
comment
Я использую .NET 4.0, поэтому я думаю, что async недоступен, верно? - person heltonbiker; 23.04.2013
comment
@heltonbiker нет, его нет в .net4 :(. Вы можете использовать только что добавленный мной альтернативный метод, который должен нормально работать для .net4 - person Viv; 23.04.2013
comment
Кроме того, если я попытаюсь get состояние устройства во время процесса инициализации, оно должно вернуть Estados.WirelessProcurando. - person heltonbiker; 23.04.2013
comment
@heltonbiker ok Я думал, что WirelessProcurando означает "Инициализированное состояние". В какое состояние вы хотите, чтобы он перешел при инициализации (т.е. после задержки) - person Viv; 23.04.2013
comment
@heltonbiker Я обновил свой ответ, чтобы установить состояние WirelessProcurando сразу при вызове, а затем после заданной задержки переключить состояние на WirelessConectado из основного потока пользовательского интерфейса. - person Viv; 23.04.2013