Итак, меня что-то беспокоило в связи с новой поддержкой асинхронности в C # 5:
Пользователь нажимает кнопку, которая запускает асинхронную операцию. Вызов немедленно возвращается, и насос сообщений снова начинает работать - вот и весь смысл.
Таким образом, пользователь может снова нажать кнопку - вызывая повторный вход. Что если это проблема?
В демонстрациях, которые я видел, они отключают кнопку перед await
вызовом и включают ее снова после. Это кажется мне очень хрупким решением в реальном приложении.
Должны ли мы кодировать какой-то конечный автомат, который указывает, какие элементы управления должны быть отключены для данного набора запущенных операций? Или есть лучший способ?
Я испытываю желание просто показать модальное диалоговое окно на время операции, но это похоже на использование кувалды.
У кого-нибудь есть какие-нибудь яркие идеи, пожалуйста?
РЕДАКТИРОВАТЬ:
Я думаю, что отключение элементов управления, которые не должны использоваться во время выполнения операции, является хрупким, потому что я думаю, что это быстро станет сложным, когда у вас будет окно с множеством элементов управления. Мне нравится быть проще, потому что это уменьшает вероятность ошибок, как во время первоначального кодирования, так и при последующем обслуживании.
Что если существует набор элементов управления, которые следует отключить для определенной операции? А что, если несколько операций выполняются одновременно?
источник
Ответы:
Давайте начнем с того, что отметим, что это уже проблема, даже без асинхронной поддержки языка. Многие вещи могут привести к тому, что сообщения будут удалены из очереди, пока вы обрабатываете событие. Вы уже должны защищаться для этой ситуации; новая языковая особенность только делает это более очевидным , что является добром.
Наиболее распространенным источником цикла сообщений, выполняющегося во время события, вызывающего нежелательный повторный вход, является
DoEvents
. Хорошая вещьawait
в противоположность томуDoEvents
, чтоawait
это повышает вероятность того, что новые задачи не будут "истощать" текущие задачи.DoEvents
прокачивает цикл сообщений, а затем синхронно вызывает повторно входящий обработчик событий, в то время какawait
обычно ставит в очередь новую задачу, чтобы она выполнялась в какой-то момент в будущем.Вы можете управлять этой сложностью так же, как и любой другой сложностью в ОО-языке: вы абстрагируете механизмы в класс и возлагаете на класс ответственность за правильную реализацию политики . (Постарайтесь, чтобы механизмы логически отличались от политики; должна быть возможность изменить политику, не повреждая слишком много механизмов.)
Если у вас есть форма с множеством элементов управления, которые взаимодействуют интересным образом, то вы живете в мире сложности своего собственного создания . Вы должны написать код, чтобы справиться с этой сложностью; если вам это не нравится, то упростите форму, чтобы она не была такой сложной.
Пользователи будут ненавидеть это.
источник
Как вы думаете, почему отключение кнопки до,
await
а затем ее повторное включение после завершения вызова является хрупким? Это потому, что вы беспокоитесь о том, что кнопка никогда не будет снова включена?Что ж, если звонок не возвращается, вы не можете позвонить снова, поэтому кажется, что вы хотите именно этого. Это указывает на состояние ошибки, которое вы должны исправить. Если время вашего асинхронного вызова истекло, это может привести к тому, что ваше приложение останется в неопределенном состоянии - опять же, в котором включение кнопки может быть опасным.
Единственный случай, когда это может запутаться, - это если несколько операций влияют на состояние кнопки, но я думаю, что такие ситуации должны быть очень редкими.
источник
Я не уверен, относится ли это к вам, так как я обычно использую WPF, но я вижу, что вы ссылаетесь на это
ViewModel
так.Вместо отключения кнопки установите
IsLoading
флаг вtrue
. Тогда любой элемент пользовательского интерфейса, который вы хотите отключить, может быть привязан к флагу. Конечным результатом является то, что задача пользовательского интерфейса - сортировать собственное включенное состояние, а не бизнес-логику.В дополнение к этому, большинство кнопок в WPF связаны с
ICommand
, и обычно я устанавливаю значениеICommand.CanExecute
равно!IsLoading
, поэтому оно автоматически предотвращает выполнение команды, когда IsLoading равно true (также отключает кнопку для меня)источник
В этом нет ничего хрупкого, и это то, что пользователь ожидает. Если пользователю нужно подождать, прежде чем снова нажать кнопку, заставьте его ждать.
Я могу видеть, как определенные сценарии управления могут принимать решение о том, какие элементы управления отключать / включать в одно и то же время довольно сложно, но удивительно ли, что управление сложным пользовательским интерфейсом будет сложным? Если у вас слишком много страшных побочных эффектов от конкретного элемента управления, вы всегда можете просто отключить всю форму и запустить «загрузку» вихревого концерта над всем этим. Если это относится к каждому отдельному элементу управления, то ваш пользовательский интерфейс, вероятно, не очень хорошо спроектирован в первую очередь (жесткая связь, плохая группировка элементов управления и т. Д.).
источник
Отключение виджета является наименее сложным и подверженным ошибкам вариантом. Вы отключаете его в обработчике перед началом асинхронной операции и снова включаете его в конце операции. Таким образом, disable + enable для каждого элемента управления остаются локальными для обработки этого элемента управления.
Вы даже можете создать оболочку, которая будет принимать делегат и элемент управления (или больше из них), отключать элементы управления и асинхронно запускать что-то, что будет выполнять делегирование и затем повторно включать элементы управления. Он должен позаботиться об исключениях (сделайте включение в, наконец), чтобы он по-прежнему работал надежно, если операция не удалась. И вы можете объединить это с добавлением сообщения в строке состояния, чтобы пользователь знал, чего он ждет.
Возможно, вы захотите добавить некоторую логику для подсчета отключений, чтобы система продолжала работать, если у вас есть две операции, которые должны отключить третью, но не являются взаимоисключающими. Вы дважды отключаете элемент управления, поэтому он снова станет активным, только если вы включите его дважды. Это должно охватывать большинство случаев, сохраняя при этом отдельные случаи настолько, насколько это возможно.
С другой стороны, что-то вроде конечного автомата станет сложным. По моему опыту, все конечные автоматы, которые я когда-либо видел, были трудны в обслуживании, и ваш конечный автомат должен был охватывать весь диалог, связывая все со всем.
источник
Хотя Ян Худек и Рэйчел высказали связанные идеи, я бы предложил использовать что-то вроде архитектуры Model-View - выразить состояние формы в объекте и связать элементы формы с этим состоянием. Это отключило бы кнопку таким образом, чтобы она могла масштабироваться для более сложных сценариев.
источник