Take the 2-minute tour ×

So, I am a systems programmer working on a UI (bad idea, maybe, but I think I do ok all things considered). Anyway, my program is an interface to a service running on a remote machine which controls a piece of hardware. The basic model is:

  1. Send command from the UI.
  2. Get ACK from remote service, enter busy state.
  3. Remote service processes request and sends OK or ERROR.
  4. UI receives response and leaves busy state.

When the UI enters a busy state all controls are disabled (well, all but one, I'll explain that shortly as it is part of the problem). One aspect of the system is to assign arbitrary data to an object (data is a string, object is irrelevant for the purposes of this question). In order to make this process robust and simple, the data will be committed to the remote server when the user presses Enter within the text area or focus leaves the text area.

This however causes a bit of a problem. One button in the UI is always in one of two states; "start" or "stop". When the system is in a busy state this button turns into a stop button and is the only enabled control in the UI. This is necessary in order to cancel long running operations.

However, imagine the following scenario:

  1. User types into text area in order to assign data to an object but does not press enter to commit.
  2. User presses the "start" button.
  3. Because the text area has now lost focus the data is committed. This causes the system to go into a busy state.
  4. After the data is sent and we are in a busy state the click handler for the start/stop button is invoked. However, the button is now in a stop state because we sent the data when the text area lost focus and we have not yet received an OK response.

If the user clicks start again everything is ok because the UI will not commit duplicate data, only committing when it has changed. However, this is a bug and I need to fix it.

I don't want to get rid of the commit on lost focus because it ensures that, even if the user does not press enter, the data on screen will always be committed and valid. Does anyone else have any ideas here? Is there perhaps a better way of going about this? I can think of some convoluted workarounds, but they are all ugly and really break the encapsulation between various parts of the UI.

So, if anyone else has a simpler, better solution I would love to hear it.

share|improve this question
I am not sure I understand what the purpose of the Start button is. Some parts of your scenario are confusing me. If you need to prevent the click during data sending, disable the start button in the same place you start sending the data, rather than waiting for the ACK to disable it. –  sirtimbly May 26 '11 at 0:56
@sirtimbly: Sorry, I left out some details in an attempt to keep the post as short as possible. This UI operates a medical device, and the "start" button initiates a long running sequence. The problem is that the click does not occur during data sending, it occurs right after as a result of the commit on lost focus. The text box loses focus before the click handler executes, and I have no way of knowing why I am busy, just that I am and that the button should stop any current operation. –  Ed S. May 26 '11 at 1:26
I also realize that this is a very specific situation and that it will be hard to find help. Just throwing this out there in case I get surprised, but I am working on it in the meantime. –  Ed S. May 26 '11 at 1:26
add comment

2 Answers

Firstly, you probably should have separate buttons for start and stop/cancel. Having the same button do opposite things is a prescription for human error whenever there is lag.

As for your particular problem, if this situation is limited to the Start button, you could leave the Start button enabled and cache the Start command, sending it after the busy state ends (until status == OK). Cache only one Start command to prevent sending multiple Start commands. Disable the Start button and clear the cache only after receiving an acknowledgement that Start specifically has been received.

I think users will find it very annoying and possibly error-inducing to have to wait between inputs for the controls to enable. I can imagine users tabbing or clicking on a control that enables and disables in a way that users don’t realize that some input was lost. Perhaps there is a more general way to handle this situation for all controls:

  • If busy times tend to be short (<2 sec) and errors very infrequent, consider queuing all the commands. If an error is received, halt sending the commands until the user supplies the fix, and insert the fix to the front of the queue then resume sending. You may also want to have some active queue management (e.g., removing earlier commands that are undone by later commands).

  • If busy times tend to be long and errors infrequent, consider batching all the commands to be sent in some reasonable order when the user clicks the Start button. Be sure to provide feedback on when the pre-start commands are all OK’ed (e.g., a “Checking Setup” progress message), so the user knows when she or he can turn away. You may also want to consider an audio alarm if there’s an error, in case the user turned away before the process actually started despite your feedback.

  • If busy times are long and errors and frequent you have an inherently clunky device that calls for a clunky UI. Consider using a wizard where users enter/accept each command one at a time, verifying each worked before moving on to the next.

share|improve this answer
Thanks for the response, there is some good feedback here. I didn't go into much detail as to what the machine actually does, but most of the commands that a user will send involve motion that must be accurate to .25um. As such, these moves can take some time. We currently do maintain a queue of commands, but if we did not disable the controls we would have to either throw away some commands as sending them would cause a "hardware busy" error. If we waited to send them until the busy state had ended we run the risk of the user clicking around more quickly than they should assuming that –  Ed S. May 26 '11 at 15:53
The system had not yet responded (the client may be used from any PC with an internet connection). That doesn't mean that disabling controls is the only/best way to go about it, so I'll rethink things a bit on that end. The start/stop button is analogous to a play/pause button on a DVD or CD player, so I'm not convinced that it is always a bad idea. The problem with caching the start command is that the click handler does not fire until after the text area loses focus and the data commit has begun. Perhaps I will simply remove the commit on lost focus entirely and do so after –  Ed S. May 26 '11 at 15:56
certain actions only which would still ensure that the data is always committed before a run is begun. Thanks again for the response. –  Ed S. May 26 '11 at 15:57
My physical DVD and CD players have separate pause and play buttons. Virtual media players get away with combined play/pause because they don't have lag. With lag, you can get users repeatedly playing/pausing if they don't notice the label changing, and they won't understand why it isn't working. Could be bad for a medical device. Definitely bad for a fire suppression system like in the story I linked to. –  Michael Zuschlag May 27 '11 at 14:44
For the caching approach, I was thinking the Loss Focus event never disables/changes the Start button --only the other controls. If stop also cancels text box data transmission, that's another good reason to have separate buttons. –  Michael Zuschlag May 27 '11 at 14:46
show 1 more comment

A simple approach that might be "good enough":

As soon as a change is made (ie on the first character press), disable the "Start" button.

Only enable the button when changes have been saved. You might want to have an explicit "Save" button as well, in case the user isn't aware of the convention that the enter key saves.

share|improve this answer
add comment

Your Answer


