Search code examples
c#winformsmessage-pump

How to encapsulate user interaction in a single method


I'm using an external framework that defines an interface with a single method:

bool Authenticate();

that is to contain the whole process of authentication, including user interaction (WinForms). What I would like to do is something like:

bool Authenticate()
{
  bool succeeded = false;
  bool userCancelled = false;
  while(!succeeded && !userCancelled)
  {
     var credentials = AskTheUserForCredentials(); // this needs to wait for user input before returning!
     if (credentials == null) 
       userCancelled = true;
     else
       succeeded = AuthenticateWithAnExternalServer(credentials);

     if (!succeeded)
       ShowErrorMessage();
  }
  return succeeded;
}

Now the easy way to implement AskTheUserForCredentials() and ShowErrorMessage() is to use Form.ShowDialog() inside. This is really bad user experience, as the dialog disappears for the actual authentication process and error message appears in a new, have-to-click-to-close dialog.

I'd rather have it all in a single form, that stays visible, disables textboxes/buttons appropriately and displays the error message by itself.

How would you do it in this single, blocking method call?

UPDATE

Best solution so far is to implement a message pump inside AskTheUserForCredentials():

Credentials AskTheUserForCredentials()
{
   while(NeitherOkNorCancelPressed())
   {
      Application.DoEvents();
      Thread.Sleep(10); // Standard Sleep(0) results in 100% procesor core usage.
   }
   return CreateCredentialsFromTextboxesEtc();
}

Now we all know message pumps are far from clean.

Exactly how bad is this solution?

Anything better?

UPDATE 2

The message pump had some pitfalls:

  • being ugly and not fully cpu-effective

  • working terribly slow with White UIAutomation

I ended up delegating the whole process to a dialog as ChrisBD (the dialog only closes after ultimate success or failure). This took more time to abstract the authentication away from the GUI with IoC, but eventually is clean and works as intended.


Solution

  • I think that you're almost there.

    Display a modal dialog that has the user input controls and have that dialog call the Authenticate method when required.

    You can then choose when the dialog should close and indeed where any error message is displayed.

    Have a property of the modal dialog class indicate whether authentication was successful or not. When you close the dialog class (which was instantiated by your main application before it opened it modally) the main application will continue to run - you can then check to see if authentication was successful or not, by checking the appropriate dialog class property.

    *edited here *

    If you're implementing the Authenticate method yourself then it is this function that calls the modal dialog, rather than your main application (as stated earlier). The custom form class can run all of your authentication logic and display your user interaction controls, along with any error messages. Remember to remove the minimize and close buttons and set a class property to indicate success or failure of the authentication process.

    The calling framework will wait for the return value of your authentication method.