All Windows users must have seen the message dialog screen many number of times. It is used to show information, a warning, an error message or ask for confirmation to certain actions which may result in loss of data or irreversible state such as formatting the disc drive etc.
As a Delphi developer we know that there is a MessageDlg function defined in the Dialogs.pas unit that displays a message dialog in the center of the screen with the message text we provide. We also know that it returns the value of the button user has selected.
Consider a scenario where user is making changes to an employee record and they hit the Close button by mistake. Since the changes may not have been saved in the database, we don't want the user to lose their changes. A typical solution is to ask the user if the record is found dirty.
if Employee.Modified then
begin
if MessageDlg('Employee details has been changed. Do you want to abandon changes?', mtConfirmation, [mbYes, mbNo], 0) = mrYes then
AbandonChanges //Throw away changes
else
SaveChanges; //Save changes
end;
The approach looks acceptable and it works as expected. So what's the mystery? I encourage you to give it a try.
Run it again, make changes to an employee record and hit the Close button. The confirmation dialog appears. Now click the small "x" button located on the top right corner of the confirmation dialog. If you like the keyboard, press the Esc key.
Surprised with the results? It goes against our intention and updates the employee record. Imagine the impact if the transaction is much more complex than above and has cascade effects. What if it is irreversible like formatting a disc or deleting a file permanently? No user will expect a system to function against them when they hit the "x" button or the Esc key.
Who's the culprit? Yes, it is the MessageDlg who plays the odd. Unexpectedly, it returns mrCancel (Huh?) even if it is not a valid choice we've given. It is the third choice user can always make. I have seen plenty of examples ignoring this fact.
A better solution is to rewrite the message text and change our focus.
if Employee.Modified then
begin
if MessageDlg('Employee details has been changed. Do you want to save changes?', mtConfirmation, [mbYes, mbNo], 0) = mrYes then
SaveChanges; //Save changes
else
AbandonChanges //Throw away changes
end;
This approach solves the problem with an assumption. The apparently hidden choice "Cancel" is treated as "No". Moreover, it also touches the UX element and changes the focus towards "Saving" rather than "Abandoning" the transaction. However, it still leaves us with a problem. If user wishes to go back to the state where they were before clicking the Close button, it is impossible.
To fulfill the user's wish, system should entertain the Cancel request more diligently. Also, from the UX perspective, it is important to show the Cancel button as valid choice. This makes user aware of their choices.
As a Delphi developer we know that there is a MessageDlg function defined in the Dialogs.pas unit that displays a message dialog in the center of the screen with the message text we provide. We also know that it returns the value of the button user has selected.
Consider a scenario where user is making changes to an employee record and they hit the Close button by mistake. Since the changes may not have been saved in the database, we don't want the user to lose their changes. A typical solution is to ask the user if the record is found dirty.
if Employee.Modified then
begin
if MessageDlg('Employee details has been changed. Do you want to abandon changes?', mtConfirmation, [mbYes, mbNo], 0) = mrYes then
AbandonChanges //Throw away changes
else
SaveChanges; //Save changes
end;
Run it again, make changes to an employee record and hit the Close button. The confirmation dialog appears. Now click the small "x" button located on the top right corner of the confirmation dialog. If you like the keyboard, press the Esc key.
Surprised with the results? It goes against our intention and updates the employee record. Imagine the impact if the transaction is much more complex than above and has cascade effects. What if it is irreversible like formatting a disc or deleting a file permanently? No user will expect a system to function against them when they hit the "x" button or the Esc key.
Who's the culprit? Yes, it is the MessageDlg who plays the odd. Unexpectedly, it returns mrCancel (Huh?) even if it is not a valid choice we've given. It is the third choice user can always make. I have seen plenty of examples ignoring this fact.
A better solution is to rewrite the message text and change our focus.
if Employee.Modified then
begin
if MessageDlg('Employee details has been changed. Do you want to save changes?', mtConfirmation, [mbYes, mbNo], 0) = mrYes then
SaveChanges; //Save changes
else
AbandonChanges //Throw away changes
end;
This approach solves the problem with an assumption. The apparently hidden choice "Cancel" is treated as "No". Moreover, it also touches the UX element and changes the focus towards "Saving" rather than "Abandoning" the transaction. However, it still leaves us with a problem. If user wishes to go back to the state where they were before clicking the Close button, it is impossible.
To fulfill the user's wish, system should entertain the Cancel request more diligently. Also, from the UX perspective, it is important to show the Cancel button as valid choice. This makes user aware of their choices.
Thanks, good article
ReplyDeleteInteresting; after 20 years of Delphi programming, I didn't know closing the dialog like that resulted in mrCancel. Not that it matters, because I must say, it's a serious bad smell programming it as in the first example. I've never (at least not in my team) seen a programmer code like that.
ReplyDeleteThe second one's absolutely the superior way to do it. It's much more 'defensive'.
Thanks for the feedback. I discovered this while reviewing code for one of a senior developer which was mix of both and it taught me a new thing.
Delete